Intended Audience: Decipher survey programmers / developers responsible for embedding fraud and bot detection into market-research studies.

Objective: Step-by-step instructions for integrating the dtect JavaScript SDK into a Decipher survey, fetching security results, persisting them via Survey.setPersistent(), and branching or terminating participants based on their dtect score and individual security flags.

Overview of Flow

Follow these steps to integrate dtect into your Decipher survey:

  1. Load custom JavaScript in the participant session via a style override (respview.client.js) in your survey.xml
  2. Initialize the dtect SDK with your clientId and apiKey
  3. Show dtect’s Quality Questions to participants, collect responses, and create a security result for the current participant.
  4. Persist the returned results in Decipher using Survey.setPersistent()
  5. Read the dtect Security Result
  6. Terminate Participants with the value returned

1. Adding the dtect’s SDK into your Decipher survey

You will need to update your survey.xml to add the dtect and start collecting data about your participants. Copy and paste the script below and replace the following variables

<aside> ⚠️

Once you paste the script, don’t forget to add values to CLIENT_ID, API_KEY, PROJECT_ID. Otherwise the API will fail

</aside>

<exec>
#creates an empty dtect Result
p.client_dtectResults = {
'dtectScore' : 'x',
'isDuplicateDevice' : 'x',
'isDuplicateIp' : 'x',
'isDuplicateId' : 'x',
'isLocationBlocked' : 'x',
'isLocationInvalid' : 'x',
'isAutomationDetected' : 'x',
'isUntrustedBrowserOrOS' : 'x',
'isBlockedIP' : 'x',
'isVpnDetected' : 'x',
'isDeviceTampered' : 'x',
'isVirtualMachine' : 'x',
'isDevToolsOpened' : 'x',
'isPrivacySettingsEnabled' : 'x',
'isTorDetected' : 'x',
'isHighActivityDevice' : 'x',
'isIncognito' : 'x',
'isAIUsageDetected' : 'x',
'isQualityRejected' : 'x'
}
</exec>

<style mode="after" name="respview.client.js" wrap="ready"><![CDATA[
const CLIENT_ID = "REPLACE_WITH_YOUR_PUBLIC_CLIENT_ID";
const API_KEY = "REPLACE_WITH_YOUR_PUBLIC_API_ID";
const PROJECT_ID = "REPLACE_WITH_YOUR_SURVEY_ID"
const RESPONDENT_TYPE = "general";
const QUESTIONS_LANGUAGE = "en";
const VISITOR_ID = "${uuid}";

let isSubmitting=false,container,overlay,originalBodyOverflow="",originalDocumentOverflow="",previousScrollPosition=null;
function injectStyles(){if(document.getElementById("dtect-styles"))return;const s=document.createElement("style");s.id="dtect-styles";s.textContent=".dtect-container{position:fixed;top:0;left:0;right:0;bottom:0;padding:40px 20px;background-color:#fff;overflow-y:auto;z-index:2147483647;display:flex;justify-content:center;align-items:flex-start;}.dtect-loading{display:flex;justify-content:center;align-items:center;padding:40px}.dtect-spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #0078d4;border-radius:50%;animation:dtect-spin 1s linear infinite}@keyframes dtect-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.dtect-q{margin-bottom:1.5em}.dtect-q-text{font-size:1.1em;font-weight:600;margin-bottom:0.5em;color:#000;}.dtect-choice label{display:flex;align-items:center;gap:8px;margin:0.4em 0;font-size:0.95em;cursor:pointer;color:#000;}.dtect-choice input[type=radio],.dtect-choice input[type=checkbox]{margin-right:8px;width:18px;height:18px;accent-color:#0078d4}.dtect-input{font-family:inherit;border:1px solid#ccc;border-radius:4px;padding:6px;width:100%;box-sizing:border-box}.dtect-textarea{resize:vertical;min-height:60px}.dtect-actions{display:flex;justify-content:flex-end;align-items:center;gap:12px}.dtect-error-text{color:#d93025;font-size:0.95em}.dtect-btn{background:#0078d4;color:#fff;border:none;padding:8px 18px;border-radius:4px;cursor:pointer;font-size:1em;margin-top:1em}.dtect-btn:hover{background:#005fa3}.dtect-btn:disabled{background:#ccc;cursor:not-allowed}";document.head.appendChild(s);}
function preparePage(){if(overlay)return;const body=document.body;const docEl=document.documentElement;if(!body)return;previousScrollPosition={left:window.pageXOffset||0,top:window.pageYOffset||0};originalBodyOverflow=body.style.overflow;body.style.overflow="hidden";if(docEl){originalDocumentOverflow=docEl.style.overflow;docEl.style.overflow="hidden";}}
function createContainer(){if(container)return container;preparePage();overlay=document.createElement("div");overlay.id="dtect-container";overlay.className="dtect-container";overlay.innerHTML='<div class="dtect-loading"><div class="dtect-spinner"></div></div>';document.body.appendChild(overlay);container=overlay;return container;}
function restoreSurvey(){if(overlay){overlay.remove();overlay=null;}container=null;const body=document.body;const docEl=document.documentElement;if(body)body.style.overflow=originalBodyOverflow;if(docEl)docEl.style.overflow=originalDocumentOverflow;originalBodyOverflow="";originalDocumentOverflow="";if(previousScrollPosition&&typeof window.scrollTo==="function")window.scrollTo(previousScrollPosition.left,previousScrollPosition.top);previousScrollPosition=null;}

async function initSDK() {
  try {
    injectStyles();
    container = createContainer();
    const dtectPackage = await import(
      "<https://unpkg.com/@dtect/security-sdk-js@latest/dist/index.mjs>"
    );

    const sdk = await dtectPackage.init({
      clientId: CLIENT_ID,
      apiKey: API_KEY,
      includeResults: true,
    });

    const securityPromise = sdk.getSecurityResult({
      projectId: PROJECT_ID,
      visitorId: VISITOR_ID,
    });
    const qs = await sdk.getQualityQuestions({
      respondentType: RESPONDENT_TYPE,
      language: QUESTIONS_LANGUAGE,
    });
    renderQuestions(qs, securityPromise, sdk);
  } catch (e) {
    restoreSurvey();
  }
}

async function submitResults(securityPromise, sdk, answers) {
  try {
    await securityPromise;
    await sdk.isAIUsageDetected({
      projectId: PROJECT_ID,
      visitorId: VISITOR_ID,
    });
    const response = await sdk.checkQualityQuestions({
      projectId: PROJECT_ID,
      visitorId: VISITOR_ID,
      questions: answers,
    });
    
    Survey?.setPersistent("client_dtectResults", response.results);
    sessionStorage.setItem("@dtect/results", JSON.stringify(response.results));
    restoreSurvey();
  } catch (err) {
    restoreSurvey();
  } finally {
    isSubmitting = false;
    restoreSurvey();
  }
}

function renderQuestions(qs,securityPromise,sdk){if(!container)return;container.innerHTML="";const wrapper=document.createElement("div");wrapper.style.maxWidth="700px";wrapper.style.margin="0 auto";wrapper.style.padding="0 0 40px";const f=document.createElement("div");qs.forEach(function(q){const b=document.createElement("div");b.className="dtect-q";const t=document.createElement("div");t.className="dtect-q-text";t.innerHTML="<strong>"+q.text+"</strong>";b.appendChild(t);const c=document.createElement("div");c.className="dtect-choice";if(q.type==="single_select"||q.type==="multi_select"){q.options.forEach(function(o){const l=document.createElement("label");const i=document.createElement("input");i.type=q.type==="multi_select"?"checkbox":"radio";i.name=q.id;i.value=o.id;i.className="dtect-input";l.appendChild(i);l.appendChild(document.createTextNode(" "+o.text));c.appendChild(l);});}else if(q.type==="open_end"){const ta=document.createElement("textarea");ta.name=q.id;ta.rows=3;ta.className="dtect-input dtect-textarea";c.appendChild(ta);}b.appendChild(c);f.appendChild(b);});const actions=document.createElement("div");actions.className="dtect-actions";const errorText=document.createElement("div");errorText.className="dtect-error-text";errorText.textContent="All questions must be answered to proceed.";errorText.style.display="none";const btn=document.createElement("button");btn.className="dtect-btn";btn.textContent="Submit";actions.appendChild(errorText);actions.appendChild(btn);f.appendChild(actions);wrapper.appendChild(f);container.appendChild(wrapper);btn.onclick=async function(e){e.preventDefault();if(isSubmitting)return;const answers=[];let allAnswered=true;qs.forEach(function(q){if(q.type==="open_end"){const field=container.querySelector('[name="'+q.id+'"]');const value=field?field.value.trim():"";if(!value){allAnswered=false;return;}answers.push({questionId:q.id,answers:[value]});}else{const selected=Array.from(container.querySelectorAll('[name="'+q.id+'"]:checked'));if(!selected.length){allAnswered=false;return;}answers.push({questionId:q.id,answers:selected.map(function(el){return el.value;})});}});if(!allAnswered){errorText.style.display="block";return;}errorText.style.display="none";isSubmitting=true;btn.disabled=true;btn.textContent="Checking…";await submitResults(securityPromise,sdk,answers);};}

if (!sessionStorage.getItem("@dtect/results")){ 
	initSDK();
}else{
	Survey?.setPersistent("client_dtectResults", JSON.parse(sessionStorage.getItem("@dtect/results")));
}
]]></style>

<html label="info1">Welcome! Please click 'continue' to proceed!</html>

<suspend/>