Integrating Reveal AI Into Other Survey Platforms

Written By revealai

Last updated 10 days ago

As mentioned in the Embed RevealAI iFrame you may want to embed a Reveal AI conversational survey into another survey platform. Maybe you’re already heavily invested in that platform or maybe you want to leverage some of the features that they supply and Reveal does not yet support. Either way we’re here to help you integrate and get the best of both worlds.

Which platforms support embedding Reveal AI?

Platform

Can Embed Reveal?

Notes

SurveyMonkey

No

Pollfish

No

Alchemer

Yes

Also supports custom javascript which is nice for things like preventing the user clicking past the chatbot

Integrating w/ Alchemer

Add the Iframe

  1. In the Reveal platform, on the Design Tab, use the Embedding Configuration button to customize your iframe setup (color, progress, titles). Make sure that you check “Use Reveal Tracking ID” and then copy the iframe code at the bottom

  2. In the Alchemer platform in your survey add a text/media block and select text/instructions from the options

  3. Paste the iframe code in and make the following edits

    1. replace &rtid=RTIDGOESHERE with rtid=[survey('session id')] . This is how the Alchemer session ID is passed into the reveal platform and stamped onto the reveal data so you can match things back up.

    2. add width and height attributes to your iframe to control the size such as width="400" and height="550" where the values are pixels

  4. All told your iframe code should look something like <iframe width="400" height="550" src="https://chat.getreveal.ai/i/revealai-demo?iframe=true&rtid=[survey('session id')]"></iframe>

  5. If you later go to edit the iframe content and see this

Then click on the three dot menu, then “source” to be able to edit the iframe code

Piping in Responses

You can use Reveals placeholder capability ( Using Placeholders ) to pass in data from Alchemer questions into the reveal survey

Step 1. Setup your placeholder in the Reveal design tab. For this example we’ll use a product-name placeholder for a question such as “why did you prefer product-name over it’s competitors?”

Step 2. Update the iframe code in Alchemer to pull the relevant question id and pass it along to the reveal iframe via the URL. In this example the id=5 part is how the Alchemer merge code knows which question response to pipe in

Example
// iframe src should look like src="https://chat.getreveal.ai/i/revealai-demo?iframe=true&rtid=[survey('session id')]&attribute-product-name=[question('value'), id='5']" >

If your responses have special characters in them you’ll want to encode them in the URL by adding urlencode='true' to the merge code

Example
// iframe src shoudl look like src="https://chat.getreveal.ai/i/revealai-demo?iframe=true&rtid=[survey('session id')]&attribute-product-name=[question('value'), id='5', urlencode='true']" >

Preventing Skipping

You can stop respondents from advancing past a page until they finish the interview

Step 1: On the survey page containing your Reveal iframe, add a JavaScript Action. Importantly you should only add one action per page

Step 2: Copy the following action code in. Notice that there is a top section for optional configuration and the bottom section does not need to be changed

Example
/** * CONFIG SECTION [OPTIONAL] * EDIT THESE VALUES IF YOU WANT TO CHANGE THE DEFAULTS */ /** * Whether to block respondents from continuing until the reveal interview is * completed. * Set to false if you want to allow users to skip the reveal interview */ var preventSkipping = true; // Tooltip shown when hovering the disabled button. var tooltipMessage = "Please complete the interview first"; /** * Alchemer's next ("Continue") and final ("Submit") button ids. Advanced * users can override with their own list of CSS selectors. */ var buttonSelectors = ["#sg_NextButton", "#sg_SubmitButton"]; /** * CODE SECTION - YOU SHOULD NOT NEED TO EDIT ANYTHING BELOW HERE * * Disables the survey's Continue / Submit button while a visible Reveal * interview iframe on the page is unfinished, and re-enables it once every * visible interview has completed. * * The standard iframe copied from the Design tab needs no changes — Reveal * iframes are detected automatically by their source URL, so multiple and * conditionally-shown interviews on one page all work with a single script. */ (function () { "use strict"; // Host of the Reveal interview app, injected when this script is served. var CHAT_HOST = "https://chat.getreveal.ai"; // How often to re-check iframe visibility/completion (ms). var POLL_INTERVAL_MS = 500; // The completion event emitted by the interview app to its parent window. var END_EVENT_TYPE = "reveal-ai-emit-end-interview"; // Iframes whose interview has completed. Keyed by the iframe element so each // interview on the page is tracked independently. var completedIframes = new Set(); var tooltipEl = null; // --- iframe discovery ----------------------------------------------------- /** All Reveal interview iframes currently in the DOM. */ function findRevealIframes() { var iframes = document.querySelectorAll("iframe"); var matches = []; for (var i = 0; i < iframes.length; i++) { var src = iframes[i].getAttribute("src") || ""; if (src.indexOf(CHAT_HOST) !== -1 && src.indexOf("/i/") !== -1) { matches.push(iframes[i]); } } return matches; } /** An iframe is visible if it renders in the layout with a non-zero box. */ function isVisible(iframe) { if (iframe.offsetParent === null) return false; var rect = iframe.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; } // --- Button enable/disable ------------------------------------------------ function eachButton(fn) { for (var i = 0; i < buttonSelectors.length; i++) { var button = document.querySelector(buttonSelectors[i]); if (button) fn(button); } } function ensureTooltip(button) { if (tooltipEl) return; tooltipEl = document.createElement("div"); tooltipEl.className = "reveal-ai-tooltip"; tooltipEl.textContent = tooltipMessage; tooltipEl.style.cssText = [ "position:absolute", "bottom:100%", "left:50%", "transform:translateX(-50%)", "background-color:#333", "color:#fff", "padding:8px 12px", "border-radius:4px", "font-size:13px", "white-space:nowrap", "margin-bottom:8px", "opacity:0", "pointer-events:none", "transition:opacity 0.2s ease", "z-index:1000", ].join(";"); var arrow = document.createElement("div"); arrow.style.cssText = [ "position:absolute", "top:100%", "left:50%", "transform:translateX(-50%)", "width:0", "height:0", "border-left:6px solid transparent", "border-right:6px solid transparent", "border-top:6px solid #333", ].join(";"); tooltipEl.appendChild(arrow); var parent = button.parentNode; if (parent) { parent.style.position = "relative"; parent.appendChild(tooltipEl); } button.addEventListener("mouseenter", function () { if (button.disabled && tooltipEl) tooltipEl.style.opacity = "1"; }); button.addEventListener("mouseleave", function () { if (tooltipEl) tooltipEl.style.opacity = "0"; }); } function disableButtons() { eachButton(function (button) { ensureTooltip(button); if (button.disabled) return; button.disabled = true; button.classList.add("sg-disabled"); button.style.opacity = "0.5"; button.style.cursor = "not-allowed"; }); } function enableButtons() { eachButton(function (button) { if (!button.disabled) return; button.disabled = false; button.classList.remove("sg-disabled"); button.style.opacity = "1"; button.style.cursor = "pointer"; }); if (tooltipEl) tooltipEl.style.opacity = "0"; } // --- Completion listener -------------------------------------------------- // A completed interview re-emits this event whenever the iframe (re)loads, // so a page refresh re-marks it complete without any local persistence. window.addEventListener("message", function (event) { if (!event.data || event.data.type !== END_EVENT_TYPE) return; var iframes = findRevealIframes(); for (var i = 0; i < iframes.length; i++) { if (iframes[i].contentWindow === event.source) { completedIframes.add(iframes[i]); return; } } }); // --- Gating loop ---------------------------------------------------------- function tick() { if (!preventSkipping) return; var iframes = findRevealIframes(); var blocking = false; for (var i = 0; i < iframes.length; i++) { if (isVisible(iframes[i]) && !completedIframes.has(iframes[i])) { blocking = true; break; } } if (blocking) disableButtons(); else enableButtons(); } setInterval(tick, POLL_INTERVAL_MS); tick(); })();

That's it. The script automatically finds the Reveal interview(s) on the page and disables the Continue (or Submit) button until every visible interview is complete.

What it handles for you:

  • Continue and Submit buttons — both are supported automatically; you don't need to tell the script which one is on the page.

  • Multiple interviews on one page — each interview is tracked independently, and the button stays disabled until all of them are done.

  • Conditionally-shown interviews — if an interview is hidden by Alchemer logic, it is ignored; only interviews the respondent can actually see are required.

  • Page refresh — a completed interview stays completed after a reload.

Advanced customization

Advanced users can tailor the behavior with these optional variables that are listed in the top configuration section

Variable Default Purpose

preventSkipping

true

Set false to disable button gating entirely. (this is also equivalent to not including the script in the first place)

pooltipMessage

"Please complete the interview first"

Text shown on hover while the button is disabled.

buttonSelectors

["#sg_NextButton", "#sg_SubmitButton"]

CSS selectors of the button(s) to gate, if your theme differs.