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.
Follow these steps to integrate dtect into your Decipher survey:
respview.client.js) in your survey.xmlclientId and apiKeySurvey.setPersistent()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/>