## embed-a-map ### Deep Linking # Deep Linking The Mappedin Shareable 3D Map supports deep linking to open a map at a specific location or a set of directions. A deep link can be constructed manually using the URL parameters described in this guide. A URL copied while using a Mappedin map can also be saved for later use. For example a user could interact with a Mappedin map to display directions and copy the URL from their browser for later use. > Refer to the Iframe Messaging Guide for more information on how to implement deep linking with a map embedded in an iframe. ## Base URL The base URL for any map link begins with: `https://app.mappedin.com/map/` `` Refers to the Id of the map to be displayed. Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa ## Link to a Floor By default the lowest above ground floor is shown, which is usually the ground floor. To link to a different floor use the `floor` parameter and pass in the `floorId`. `https://app.mappedin.com/map/?floor=` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?floor=m_cbad244ceb3c4275 ## Link to a Location To link to a specific location, such as an Annotation, Point of Interest, Room or Space, use the `location` parameter and pass it the location Id, name or external ID. It is recommended to use the location name or external ID to ensure the link is stable because location Ids can change. `https://app.mappedin.com/map/?location=` Example using External Id: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?location=s_c550a911f7112193?location=gym-external-id Example using Location Name: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?location=Washroom Example using Location Id: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?location=s_c550a911f7112193 ## You Are Here A specific coordinate can be selected as “You are here”. This will show a special marker on the map, and allow users to see and to navigate using this coordinate at all times. Right click on the map to view a pop up that shows the latitude and longitude of the location clicked. To generate a deep link with a “You are here” coordinate, specify the `you-are-here query` parameter in the form of `you-are-here=%2C%2C`. `https://app.mappedin.com/map/66686f1af06f04000b18b8fa?floor=m_bfb78ebc356d2ed0&you-are-here=43.64652178%2C-79.38645187%2Cm_bfb78ebc356d2ed0` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?floor=m_bfb78ebc356d2ed0&you-are-here=43.64652178%2C-79.38645187%2Cm_bfb78ebc356d2ed0 ## Set the Starting Camera Orientation Mappedin calculates a default camera orientation to best view the entire map. The default orientation is used when the map loads, and whenever the camera is reset. To override the default orientation, `bearing` and `pitch` query parameters can be specified. These parameters are also used to reset the map when kiosk mode is enabled. - `bearing` controls how the camera is rotated horizontally relative to the map. The bearing should be a value between 0 and 360 degrees, representing a full rotation of the camera around the map. - `pitch` controls how the camera is rotated vertically relative to the map. 0 pitch means the camera is pointed top-down, while a maximum pitch of 68 degrees positions the camera nearly flat on the ground. A pitch greater than 68 degrees would make the make difficult to use. `https://app.mappedin.com/map/?bearing=&pitch=` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?bearing=45&pitch=30 ## Lock the Camera Orientation Mappedin allows panning, pitching, and rotating the bearing of the camera. To only allow panning, and prevent the pitch and bearing from being changed, set the `camera-mode` query parameter to `pan-only`. `https://app.mappedin.com/map/?camera-mode=pan-only` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?camera-mode=pan-only This can be combined with the `pitch` parameter to lock the map in a top down view. Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?camera-mode=pan-only&pitch=0 ## Wayfinding Without a Departure Location A location on the map can be linked as the destination for wayfinding. The user can then select a departure point and generate directions. When using a desktop browser, the user is prompted to drop a pin to set their departure location. When using a mobile browser the user is prompted to select another location. To link to a destination, add `/directions` to the base URL in combination with the location query parameter. `https://app.mappedin.com/map//directions?location=` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa/directions?location=s_c550a911f7112193 ## Wayfinding with a Departure Location To build a link with directions from a specific location to another location, add `/directions` to the base URL and set the `departure` and `location` to a location Id or location name. `https://app.mappedin.com/map//directions?location=&departure=` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa/directions?location=s_c550a911f7112193&departure=s_fea8f06e5dc5728d ## Wayfinding using Coordinates To create a link for wayfinding from an arbitrary coordinate on the map, add `/directions` to the base URL and specify the coordinate as the `departure` and set `location` to a location Id or location name. Right click on the map to view a pop up that shows the latitude and longitude of the location clicked. `https://app.mappedin.com/map//directions?location=&departure=%2C%2C` Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa/directions?location=s_bc5349f37474ed3d&departure=43.64670758391629%2C-79.38658289339867%2Cm_bfb78ebc356d2ed0 ## Disable Outdoors The outdoor map can be disabled, which results in only the indoor map being shown. Use `outdoors=false` to disable it. It is not required to set `outdoors` to true because outdoor map is shown by default. `https://app.mappedin.com/map/660c0c097c0c4fe5b4cc484b?floor=m_e101ad27d86783ed&outdoors=false` Example: https://app.mappedin.com/map/660c0c097c0c4fe5b4cc484b?floor=m_e101ad27d86783ed&outdoors=false ## Using QR Codes A QR code can be created that providers users with something they can scan on their smartphone that opens a Mappedin Map in their browser using any of the deep links described above. The example below demonstrates how to use Mappedin JS to generate a QR code that when scanned, opens a Mappedin Map with the directions show in the example. ## Kiosk mode !Kiosk Mode > Kiosk mode is currently available in the Mappedin free tier as a preview feature. A Mappedin Shareable 3D Map can be configured to run in kiosk mode. When kiosk mode is enabled, after 60 seconds of inactivity, Mappedin will prompt to ask if the user is still there. If there is no response within 10 seconds, the map's position will be reset to the default starting position. When using kiosk mode, it is recommended to lock the device so that only the browser displaying a Mappedin Map is visible. This can be done using Screen Pinning on Android or Guided Access on iOS along with a browser that is configured to prevent the user from leaving the map page. When multiple kiosks are available in a large venue, it may be desired to focus the map on the kiosk's location instead of showing the entire map. Refer to the Set the Starting Camera Orientation section for more information. Kiosk mode is enabled by setting the `kiosk` query parameter to `true`. `https://app.mappedin.com/map/?kiosk=true` > In the example below, the map must be interacted with to change it from its default state before the kiosk prompt will appear. Example: https://app.mappedin.com/map/66686f1af06f04000b18b8fa?kiosk=true ### Embedding a Map in HTML # Embedding a Map in HTML ## Get A Map Embed Code ## Embed the Map Code 1. Open the HTML file with any text editor (e.g., Notepad, VSCode) 2. Paste the embed code. Insert the copied `` code into the desired location within the `` of the HTML file. Example: ```html title=index.html Embedding a Mappedin Map in HTML

Welcome to Our Location

Check out our map below:

``` When embedding the viewer, the iframe should allow the following permissions, which were used in the example above: - `clipboard-write 'self'` https://app.mappedin.com to allow the app to copy content to the clipboard - `web-share 'self'` https://app.mappedin.com to allow the app to access a phone's native Share API on mobile Example Output: ### Embedding a Map with Drupal # Embedding a Map with Drupal ## Get A Map Embed Code ## Embed the Map Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page on a web site created using Drupal. 1. In the Drupal Editor, open the page to embed the map. 2. Under Content, click the 3 dots and change formatting to `Source`. 3. Paste the Mappedin embed code. 4. Click `Save` or `Preview` to preview the page with the map. The map should now appear on the web page similar to the screen shot below. !Drupal Map Embedded ### Not seeing the map? Drupal may filter `` tags for security reasons. To allow iframes in content, follow these steps: 1. Go to `Configuration` > `Content Authoring` > `Text formats and editor` 2. Select `Content` and click `Configure` 3. Under `Enabled filters`, disable the toggle for `Limit allowed HTML tags and correct faulty HTML` filter. 4. Save configuration. ### Embedding a Map with Google Sites # Embedding a Map with Google Sites ## Get A Map Embed Code ## Embed the Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page on your web site created using Google Sites. ```ts const venue = getVenue(); ``` 1. In the Google Sites website builder, open the desired page to embed the map. 2. From the `Insert` menu on the right hand side, select `Embed`. 3. Paste the embed code copied from Mappedin Maker into the window that appears. > **Recommended**: Replace `height="650"` with `height="100%"` to dynamically scale the map height to fit the available space. 4. Click "Next" and the map should appear in the view. If everything looks correct, press "Insert" to add the map to the page. The map should now appear on the web page similar to the screen shot below. !GoogleSites map embedded ### Embedding a Map with Sharepoint # Embedding a Map with Sharepoint ## Get A Map Embed Code ## Embed the Map Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page in Sharepoint. 1. Within Sharepoint, open the page to embed the map. 2. In the Toolbox, click **See all web parts**. 3. In the search bar, search for **Embed**. 4. Click **Embed**. 5. Paste the Mappedin embed code into the **Website address or embed code** field. 6. Click the X on the Embed properties box to close it. The map should now appear on the web page similar to the screen shot below. !Sharepoint Map Embedded ### Embedding a Map with Squarespace # Embedding a Map with Squarespace ## Get A Map Embed Code ## Embed the Map Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page on a web site created using Squarespace. 1. In the Squarespace editor, click the `+ Add Block` button. !Squarespace Add Block Button 2. Search for `Code`. 3. Click on ` Code`. 4. Place and resize the code block on the web page. 5. Click the Pencil icon for the code block to edit it. 6. In the Content window, replace `Hello, World!` with the Mappedin embed code. The map should now appear on the web page similar to the screen shot below. !Squarespace map embedded ### Embedding a Map with Webflow # Embedding a Map with Webflow ## Get A Map Embed Code ## Embed the Map Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page on a web site created using Webflow. 1. In the Webflow Editor, open the page to embed the map. 2. Click the `Add Elements` button. !Webflow Add Element Button 3. Click on the `Embed` element. 4. Paste the Mappedin embed code. 5. Press `Save & Close`. The map should now appear on the web page similar to the screen shot below. !Webflow Map Embeded ### Embedding a Map with Wix # Embedding a Map with Wix ## Get A Map Embed Code ## Embed the Map Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page on a web site created using Wix. 1. In the Wix Editor, open the page to embed the map. 2. Click the `Add Elements` button in the editor. 3. Click `Embed Code`. 4. Click `Embed HTML`. 5. Paste the Mappedin embed code. 6. Press `Update`. 7. If your map appears cut off at the top or bottom, modify the embed code by changing `height="100%"` to `height="100%"`. 8. Press `Update`. The screenshot below shows an example of the embed code modified in step 7 to use 100% of available height. The map should now appear on the web page similar to the screen shot below. !Wix Map Embedded ### Embedding a Map with WordPress # Embedding a Map with WordPress ## Get A Map Embed Code ## Embed the Map Code Follow the steps below to embed a map as it appears in the Mappedin Viewer into a page on a web site created using Wordpress. 1. Click the Add Block button. 2. Click on `Browse All`. 3. Search for `Custom HTML`. 4. Click on `Custom HTML`. 5. In the box that says `Write HTML` paste the Mappedin embed code. 6. Click on the `Preview` tab to preview the embedded map. 7. If the map looks correct press Publish to publish the updates to the page. The map should now appear on the web page similar to the screen shot below. !Wordpress Map Embedded ### Iframe Messaging # Iframe Messaging The Mappedin viewer provides a messaging API for communication between an `` and its parent window. This allows the parent window to send messages to update the map and the map to send messages back to the parent window, alerting it of changes to the map's state. ## Events When certain events occur in the viewer, the viewer will post a message using postMessage() to the parent window containing data with the following structure. ```json { type: "", payload?: "" } ``` The viewer currently informs the parent window of the following events. ### App Loaded Event The viewer sends an `app-loaded` message after it has initialized within the iframe. The loading spinner has not necessarily gone away, but the app is ready to receive messages from the parent window. ```json { "type": "app-loaded" } ``` ### State Changed Events Whenever map state, floor, building, selected location, or departure location changes, the map viewer will send a state-changed message. - `state` is either `/` or `/directions` indicating whether the map is in the default or directions state. - `floor` is the ID of the current floor. - `building` is the ID of the current building. - `location` is an array of ID strings corresponding to all selected locations. If there is no selected location, this will be `undefined`. - `departure` is an array of ID strings corresponding to all chosen departure locations. If there is no departure location, this will be `undefined`. Only the closest departure will be chosen as the starting point for navigation. ```json { type: "state-changed", payload: { state: "", floor: "", building: "", location: ""[] | undefined, departure: ""[] | undefined, } } ``` ## Sending Commands The parent window can also issue commands by posting messages to the `` containing the viewer. Command messages should use the following structure. ```json { type: "", payload?: "" } ``` The viewer will listen for these commands and react accordingly. The viewer currently understands the following commands. ### Set State Command Use the `set-state` command to update the map `state`, `floor`, `building`, selected `location`, or `departure` location. The `set-state` command allows setting any or all of these values. - `state` should be either `/` or `/directions` indicating either default or directions state. - `floor` is the ID or name of the desired floor. - `building` is the ID or name of the desired building if the map contains multiple buildings. If floor is provided, building will be ignored in favor of the specified floor's building. - `location` is an ID or name string (or an array of ID or name strings) corresponding to all locations to be selected. - `departure` is an ID or name string (or an array of ID or name strings) corresponding to all locations that could be departures. Only the closest departure will be chosen as the starting point for navigation. ```json { type: "set-state", payload: { state?: "", floor?: "", building?: "", location?: "" | "[], departure?: ""[], } } ``` ## Deep Linking Within an Iframe > Refer to the Deep Linking documentation for a list of deep link parameters. When the viewer is embedded within an ``, it cannot access the parent window's URL due to CORS policies. This means that deep linking does not work with the embedded map unless the viewer's messaging capabilities are implemented. To ensure a smooth experience out of the box, when sharing a link using the built in Sharing button, the shared URL will always be to the full viewer even if the map is currently embedded. Users can also always open the full viewer using the button in the top right. To support deep linking within an embedded map, communication between the parent window and the viewer must be implemented. This can be done using the messaging and events APIs described above. The following example demonstrates how to implement deep linking with a map embedded in an iframe. ```ts /** * Deep Linking */ // Set the initial iframe URL based on the hash const hashParams = new URLSearchParams(window.location.hash.slice(1)); const state = hashParams.get('state') ?? '/'; const location = hashParams.get('location'); const departure = hashParams.get('departure'); const floor = hashParams.get('floor'); const building = hashParams.get('building'); const url = new URL(`${IFRAME_BASE_URL}${state}`); url.searchParams.set('embedded', 'true'); if (location != null) { url.searchParams.set('location', location); } if (departure != null) { url.searchParams.set('departure', departure); } if (floor != null) { url.searchParams.set('floor', floor); } if (building != null) { url.searchParams.set('building', building); } iframe.src = url.toString(); // Handle messages from the iframe to update the URL window.addEventListener('message', (event) => { if (event.data.type === 'state-changed') { const { state, location, departure, floor, building } = event.data.payload; let hashParams = new URLSearchParams(); if (state != null) { hashParams.append('state', state); } if (location != null) { hashParams.append('location', location.join(',')); } if (departure != null) { hashParams.append('departure', departure.join(',')); } if (floor != null) { hashParams.append('floor', floor); } if (building != null) { hashParams.append('building', building); } window.location.hash = hashParams.toString(); } }); ``` ## Example The example below implements all of the concepts described in this guide. Note how the parent window's URL is updated when the map's state changes. ### Mappedin Plugin for WordPress # Mappedin Plugin for WordPress The following guide explains how to install and use the Mappedin Plugin for WordPress. The Mappedin plugin for WordPress allows users to easily add interactive indoor maps to WordPress pages using a simple shortcode, no coding required! Mappedin is a fully packaged solution that can be easily embedded into your website. Once it's configured, you will have ongoing access to Mappedin as it continuously evolves and improves with new features and data driven learnings. Customers using Mappedin will always have the best version of our product, with out of the box features including: - An interactive 3D map - Location search and point of interest listing - Turn by turn directions - Safety annotations - ... and more! ## Prerequisites - WordPress Version: 3.6.0+ - PHP Version: 7.2+ ## Installation The Mappedin Plugin for WordPress can be obtained from the WordPress Plugin Store or downloaded directly from GitHub. - WordPress Store: Mappedin Plugin for WordPress - GitHub: Mappedin Plugin for WordPress Please refer to the WordPress plugin installation documentation and follow the steps for your preferred method of installation. ## Map Configuration Mappedin users will need to provide their Map URL. Contact Mappedin if you have any questions regarding the information required for these settings. ### How to get your Map URL 1. Log into Mappedin Maker 2. Click the green Preview button. 3. Click Share. 4. Enable sharing. 5. Click the Copy Link button. The map URL is now copied to your clipboard. ### How to configure the Mappedin Plugin 1. Open your WordPress Admin Dashboard. 2. Choose Mappedin from the left admin menu. 3. Paste your Map URL. 4. Click Save. !Mappedin plugin for WordPress Configuration ### Usage The Mappedin Plugin for WordPress allows the embedding of Mappedin into a WordPress page or post using the `[mappedin]` shortcode. The steps below explain this process. 1. Click on the Add Block button. 2. Select Shortcode !Mappedin Web Plugin for WordPress Add Shortcode 3. Use `[mappedin]` as the shortcode. !Mappedin Plugin for WordPress Shortcode ### Private Maps # Private Maps > The Private Map feature is available to Mappedin Pro customers. Refer to the Pricing Page for more information. ## Overview When a Mappedin Map is set to Live, it is accessible to the public. It may be required to restrict access to a map for security or privacy reasons. This can be achieved by leaving the map in Draft mode and configuring the Mappedin Viewer to authenticate using an access token. The access token is generated by an authentication proxy that is deployed within your own infrastructure and can be protected by any authentication method, such as requiring the user to be logged in or connected to a VPN. Test out a the private map feature in the Private Map Showcase Demo. ### Authentication Flow The following diagram explains the authentication flow when using a private map. 1. A user opens Mappedin Viewer to load a map. 2. The Mappedin Viewer attempts to access the authentication proxy to request an access token using the provided URL. 3. An authentication method of your choice is used to validate the user and grant access to the authentication proxy. 4. A request is made to the authentication proxy to generate an access token. 5. The authentication proxy uses Mappedin API Keys to request an access token from the Mappedin API Key REST API. 6. The authentication proxy receives the access token and returns it to the Mappedin Viewer. 7. The Mappedin Viewer uses the access token to download the map from Mappedin's infrastructure. {/* This diagram was created by Mark Sohm using online visual paradigm here: https://online.visual-paradigm.com/w/pzcqivbh/diagrams/#diagram:workspace=pzcqivbh&proj=0&id=1&type=NetworkDiagram&width=11&height=8.5&unit=inch */} !Authentication Diagram When using this method, the Mappedin API Key used to generate the access token is stored securely in your own infrastructure and not exposed to the user. The access token is only valid for a short period of time. > The Mappedin Viewer will cache and reuse the access token for the duration of it's validity period. It will not request a new access token from the authentication proxy until the current access token expires. ## Authentication Proxy The self hosted authentication proxy is used by the Mappedin Viewer to request an access token. It is passed to the Mappedin Viewer as a query parameter: ``` https://app.mappedin.com/map/?authUrl=https:/// ``` The authentication proxy domain must be whitelisted by Mappedin before it can be used. Please contact Mappedin Support to whitelist your domain. Mappedin Viewer will make a request to the authentication proxy domain with `api/api-key/token` appended to the domain. This results in a request to: ``` https:///api/api-key/token ``` Ensure that your authentication proxy is configured to accept requests to the `api/api-key/token` endpoint. Custom endpoints are not currently supported. The authentication proxy performs the following steps: 1. Receives an HTTP POST request from the Mappedin Viewer. 2. Uses a Mappedin API Key and secret to request an access token from the Mappedin API Key REST API. 3. Returns the access token to the Mappedin Viewer. ## Authentication Proxy Examples The following examples show how to implement an authentication proxy using various technologies. These proxy examples do not authenticate the user or prevent someone from accessing them. Any authentication and or access control method can be implemented in the proxy or in another service running in front of it. ### Node.js express The following example demonstrates how to implement an authentication proxy using Node.js and the express framework. ```ts import express from 'express'; import cors from 'cors'; const app = express(); const port = 443; app.use( cors({ origin: 'https://app.mappedin.com', allowedHeaders: ['*'], }) ); app.post('/api/api-key/token', async (req, res) => { const response = await fetch('https://app.mappedin.com/api/v1/api-key/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', }), }); const data = await response.json(); return res.json(data); }); app.listen(port, () => { console.log(`Sandbox listening on port ${port}`); }); ``` ### Azure Functions The following example demonstrates how to implement an authentication proxy using Azure Functions. ```ts // Azure function const { app } = require('@azure/functions'); async function getToken(request, context) { // Handle preflight requests if (request.method === 'OPTIONS') { return { status: 204, headers: { 'Access-Control-Allow-Origin': 'https://app.mappedin.com', 'Access-Control-Allow-Headers': '*', } }; } const response = await fetch('https://app.mappedin.com/api/v1/api-key/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', }), }); const data = await response.json(); return { status: response.status, jsonBody: data, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'https://app.mappedin.com', }, }; } exports.getToken = getToken; app.http('api-key/token', { methods: ['POST', 'OPTIONS'], authLevel: 'anonymous', handler: getToken, }); ``` ### NGINX Reverse Proxy The following example demonstrates how to implement an authentication proxy using an nginx reverse proxy. ```ts location /token { # Handle preflight requests if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' 'https://app.mappedin.com' always; add_header 'Access-Control-Allow-Headers' '*' always; add_header 'Content-Length' 0; add_header 'Content-Type' 'text/plain'; return 204; } # Add CORS headers for actual requests add_header 'Access-Control-Allow-Origin' 'https://app.mappedin.com' always; proxy_pass https://app.mappedin.com/api/v1/api-key/token; proxy_method POST; proxy_set_header Accept application/json; proxy_set_header Content-Type application/json; proxy_ssl_server_name on; proxy_set_body '{"key": "${MAPPEDIN_API_KEY}", "secret": "${MAPPEDIN_API_SECRET}"}'; } ``` ### Cloudflare Workers The following example demonstrates how to implement an authentication proxy using Cloudflare Workers. ```ts export default { async fetch(request, env, ctx) { // Handle OPTIONS preflight requests if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': 'https://app.mappedin.com', 'Access-Control-Allow-Headers': '*', } }); } // Handle actual API requests (any method to this endpoint) const response = await fetch('https://app.mappedin.com/api/v1/api-key/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', }), }); // Clone the response and add CORS headers const responseData = await response.json(); const newResponse = new Response(JSON.stringify(responseData), { status: response.status, headers: { 'Access-Control-Allow-Origin': 'https://app.mappedin.com', } }); return newResponse; }, }; ``` ### AWS Lambda The following example demonstrates how to implement an authentication proxy using AWS Lambda. ```ts // AWS export const handler = async event => { // Handle OPTIONS preflight requests if (event.httpMethod === 'OPTIONS') { return { statusCode: 204, headers: { 'Access-Control-Allow-Origin': 'https://app.mappedin.com', 'Access-Control-Allow-Headers': '*', }, body: null }; } // Handle actual API requests const response = await fetch('https://app.mappedin.com/api/v1/api-key/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ key: '$KEY', secret: '$SECRET', }), }); const token = await response.json(); // Return response with CORS headers return { statusCode: response.status, headers: { 'Access-Control-Allow-Origin': 'https://app.mappedin.com', 'Content-Type': 'application/json', }, body: JSON.stringify(token) }; }; ```