Survey integrations
Add VerifyHuman to the survey platform you already use. Pick your platform from the table below and follow its one-page recipe. The recommended setup is the same everywhere: you create a link in the VerifyHuman dashboard and paste it into your survey as the starting point. That's the whole job.
Do I need a developer?
No — not for the recommended setup on any platform. The no-code survey link path works on every platform in this guide. You make a link in the dashboard, paste it in as your survey's entry point, and you're done. No code, no developer.
The optional inline snippet puts the check inside a survey question instead of on a separate page. It is a small copy-paste block of JavaScript, it is offered on only five platforms (Qualtrics, Forsta, Alchemer, LimeSurvey, Sawtooth), and it is never required — the survey link does the same job. Anything below labelled “Option B”, “optional”, or “for developers” is polish you can skip.
Which path should I use?
There's one technical fact that decides everything:
The live webcam check needs to run on a normal, full web page. Browsers block or limit the camera inside the small embedded frames that some survey tools use to display a survey. So the camera works reliably in exactly two places: (1) on a page that is the survey platform's own site and allows custom code, or (2) on a VerifyHuman-hosted page that we control.
That gives two strategies:
- Survey link (verify first) — works everywhere. Send respondents to a VerifyHuman-hosted verification page before the survey. They verify there (the camera works), and on pass we hand them into your survey carrying a signed result. This is the universal path and the easiest to set up. It is the only option on SurveyMonkey, Typeform, Jotform, Cint, and Dynata.
- On your page — no extra page for the respondent. Paste a small snippet into the survey so the check appears inside a question page. This is only possible on platforms where the survey runs on the platform's own site and allows custom JavaScript: Qualtrics, Forsta, Alchemer, LimeSurvey, Sawtooth.
When in doubt, use Survey link (verify first). It stops bad respondents before they use up your quota, needs no code, and is the same to explain to anyone: “paste this link as your survey's entry point.”
Platform index
| Platform | Recommended path | Custom JS? | Jump to |
|---|---|---|---|
| Qualtrics | On your page (or Redirect) | Yes | Qualtrics |
| Forsta / Decipher | On your page (or Redirect) | Yes | Forsta |
| Alchemer | On your page (or Redirect) | Yes | Alchemer |
| LimeSurvey | On your page (or Redirect) | Yes | LimeSurvey |
| Sawtooth (Lighthouse) | On your page (or Redirect) | Yes | Sawtooth |
| SurveyMonkey | Survey link (verify first) | No | SurveyMonkey |
| Typeform | Survey link (verify first) | No | Typeform |
| Jotform | Survey link (verify first) | No | Jotform |
| Cint | Panel router (verify-first screener) | No | Cint |
| Dynata | Panel router (verify-first screener) | No | Dynata |
Two ways to add VerifyHuman
A. No-code survey link (works on every platform)
- In the VerifyHuman dashboard, open your Project → “Survey integrations” tab and click Create link.
- Choose the platform and the type of link:
- Verify first, then survey (recommended): verify, then enter the survey.
- Survey first, then verify: finish the survey, then verify (use this when verification should gate reward payout rather than entry).
- On your page: only offered for the five custom-JS platforms (see B below).
- Set the Pass redirect URL (where verified respondents go — usually your survey link) and the Fail redirect URL (where rejected respondents go — a screenout / disqualification page).
- (Panel studies only) pick a Cint or Dynata preset and fill in the respondent-id parameter and the screenout / quota-full / security-terminate URLs. See Cint / Dynata below.
- (Optional) list any passthrough params — values arriving in the link's query string that you want passed on to the redirect and the webhook (e.g. a panel RID).
- Save. You get a shareable link:
The respondent verifies on that VerifyHuman-hosted page, then is auto-redirected to your pass or fail URL carrying a signedhttps://vhuman.riwi.com/v/<linkId>vh_token. You paste thisv/<linkId>URL into your platform at the spot named in that platform's section below (survey entry point, redirect field, or panel router entry link).
The result comes back two ways: as the signed vh_token appended to your pass URL (check its signature with our public keys, and that aud is your project ID and exp has not passed; it carries decision, status, risk_score, uniqueness_score, the session ID in sub and a receipt_id), and via the verification.completed / verification.scored webhook.
B. On your page — inline snippet (Qualtrics, Forsta, Alchemer, LimeSurvey, Sawtooth only)
The check appears inside a survey question. Each of those five sections below gives you a copy-paste snippet. All of them:
- Load the SDK from the CDN:
<script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"></script> - Start it, using your Project ID as the
siteKey(it is shown on the project page in the dashboard):VH.mount(element, { siteKey: "<projectId>", onResult: function (r) { /* gate the Next button, store the verdict */ }, }); - Gate the platform's Next / Submit button until the result is
pass(orscored), and write the result into a response field.
One required step for on-your-page snippets: add the survey vendor's domain to your project's Allowed Domains (dashboard → Project → Settings), or the check will be refused. Each section names the exact domain.
Qualtrics
Recommended path: On your page (Qualtrics runs surveys on its own site and supports custom JavaScript). Survey link (verify first) also works and needs no code.
Prerequisites for the inline path: your Qualtrics license must have Allow JavaScript enabled (a paid-license feature; ask your brand admin).
There is no iframe variant. The hosted /v/<linkId> page is a top-level navigation target and is served with X-Frame-Options: DENY — framing it will be blocked by the browser. Use the redirect path (Option A) or the inline SDK (Option B); a cross-origin iframe would also need the parent to grant allow="camera", which most survey platforms do not.
Option A — Survey link (no code)
- In the dashboard: Project → Survey integrations → Create link. Platform = Qualtrics, integration type = Verify first, then survey.
- Set Pass redirect URL to your Qualtrics survey's anonymous link, and Fail redirect URL to a disqualification page. Save → copy
https://vhuman.riwi.com/v/<linkId>. - Distribute that
v/<linkId>link as your study's entry point (in place of the raw Qualtrics link). Respondents verify first, then land in the survey. - Read the result back in Qualtrics: set the Pass redirect URL to your survey link with
?vh_verdict=passadded; we append?vh_token=...to it. In Survey Flow add an Embedded Data element with a blank field namedvh_verdictbefore your first block — Qualtrics captures the matching query param automatically.
Option B — On your page
Injection points:
- Load the SDK once: Look & Feel → General → Header → Source (<>) and paste the
<script>tag. - Declare the field: in Survey Flow, add an Embedded Data element with a blank field
vh_verdict. - Mount + gate: on your verification question, open Question behavior → JavaScript and paste the snippet.
Header (loads the SDK):
<script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"></script>Question JavaScript (starts the check, holds the Next button, stores the result):
Qualtrics.SurveyEngine.addOnReady(function () {
var self = this;
var container = this.questionContainer;
var done = false;
// Set to true ONLY after adding a branch on the verdict that screens out
// fail / score_blocked (e.g. End of Survey). Until then a failed respondent
// is held here with a message instead of continuing.
var RELEASE_ON_FAIL = false;
function released(verdict) {
return verdict === "pass" || verdict === "scored" || verdict === "review" ||
verdict === "error" || RELEASE_ON_FAIL;
}
var HELD_MESSAGE = "<p>This verification did not pass, so the survey cannot continue.</p>";
self.disableNextButton();
function finish(verdict) {
if (done) return;
done = true;
// New Survey-Taking Experience: must use setJSEmbeddedData
Qualtrics.SurveyEngine.setJSEmbeddedData("vh_verdict", verdict);
if (released(verdict)) self.enableNextButton();
else container.insertAdjacentHTML("beforeend", HELD_MESSAGE);
}
VH.mount(container, {
siteKey: "<projectId>",
// A STABLE panelist id from your entry link (declare panelist_id as
// Embedded Data in Survey Flow). Falls back to ResponseID when it is not
// set — an empty id would put every respondent under one identity.
respondentId: "${e://Field/panelist_id}" || "${e://Field/ResponseID}",
onResult: function (r) { finish(r.status); },
}).catch(function (err) {
finish(err && (err.code === "E020" || err.code === "E021") ? "error" : "fail");
});
});Add Qualtrics's survey domain (e.g. *.qualtrics.com) under your project's Allowed Domains in the dashboard, or the check is refused.
Gotchas
- New Survey-Taking Experience (the 2026 default):
setEmbeddedData()silently fails — you must usesetJSEmbeddedData(), and when you pipe or reference the value elsewhere it's named__js_vh_verdict, notvh_verdict. Declare the plainvh_verdictfield in Survey Flow first. - jQuery is no longer preloaded in the new experience. Our SDK is vanilla JS so it's fine — but don't paste jQuery-dependent helper code alongside it.
- Embedded collector caveat: capturing inbound
?vh_verdict=only works on the standard survey link, not when the survey is embedded elsewhere. - For a server-side source of truth, add a Workflows → Web Service Task that calls VerifyHuman on completion, or rely on the webhook.
Forsta / Decipher
Recommended path: On your page — Forsta/Decipher is the most open platform for custom code (it even ships a native video element, so the camera works reliably on its pages). Survey link (verify first) also works and needs no code.
Prerequisites: none for the client-side on-your-page snippet below. Only if you want Forsta's in-survey server call (v2SendRequest) to reach VerifyHuman, ask Forsta Support to add our domain to your request_allowed allowlist — but that's optional; the client SDK + button gate below needs no allowlist.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Forsta / Decipher, type = Verify first, then survey.
- Pass redirect URL = your survey URL; Fail redirect URL = a terminate page. Save → copy
https://vhuman.riwi.com/v/<linkId>. - Use the
v/<linkId>link as the study entry point. On return, capture the result with a Survey Redirect / URL variable ([url ...]inbound) and store it into a Hidden Question.
Option B — On your page
Injection points:
- Load the SDK: upload
verifyhuman.umd.jsto the survey's static directory and reference it viass:customJS, or inject it at runtime from an<html>element using the CDN tag below. - Mount + gate: add an
<html>element with a mount target (<div id="vh">) and the script. Hide Forsta's continue button (#btn_continue) until pass.
<div id="vh"></div>
<script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"></script>
<script>
var done = false;
// Set to true ONLY after adding a branch on the verdict that screens out
// fail / score_blocked (e.g. End of Survey). Until then a failed respondent
// is held here with a message instead of continuing.
var RELEASE_ON_FAIL = false;
function released(verdict) {
return verdict === "pass" || verdict === "scored" || verdict === "review" ||
verdict === "error" || RELEASE_ON_FAIL;
}
var HELD_MESSAGE = "<p>This verification did not pass, so the survey cannot continue.</p>";
document.querySelector("#btn_continue").style.display = "none";
function finish(verdict) {
if (done) return;
done = true;
// store the verdict in a hidden question field
var input = document.querySelector('input[name="vh_verdict"]');
if (input) input.value = verdict;
if (released(verdict)) document.querySelector("#btn_continue").style.display = "";
else document.querySelector("#vh").insertAdjacentHTML("beforeend", HELD_MESSAGE);
}
VH.mount("#vh", {
siteKey: "<projectId>",
onResult: function (r) { finish(r.status); },
}).catch(function (err) {
finish(err && (err.code === "E020" || err.code === "E021") ? "error" : "fail");
});
</script>Store the result: create a Hidden Question (e.g. vh_verdict) and let the snippet write into its .val input (shown above), or set it server-side via an <exec> Python block. Pipe it onward with [pipe: vh_verdict].
Add your Forsta survey domain to the project's Allowed Domains in the dashboard before going live.
Gotchas
#btn_continueis Forsta's continue button id — hide it on load, reveal it only on pass. Double-check the id on your theme if you've customized it.v2SendRequestneeds therequest_allowedallowlist (a Forsta Support ticket). For a no-touch setup, skip it — the client SDK + button gate + hidden-question write is enough; use our webhook for the server-side source of truth.- The REST
responses/dataendpoint is read+write if you prefer to backfill the result server-side.
Alchemer
Recommended path: On your page (Alchemer runs surveys on its own site, so the camera works without extra frame settings). Survey link (verify first) also works and needs no code.
Prerequisites for the inline path: the account's Integrations permission must be enabled (ask your Alchemer admin). Note that custom JavaScript is “outside normal support” scope on Alchemer — that's expected; the snippet below is self-contained.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Alchemer, type = Verify first, then survey.
- Pass redirect URL = your survey link; Fail redirect URL = a disqualification page. Save → copy
https://vhuman.riwi.com/v/<linkId>. - Use the
v/<linkId>link as the study entry point. On the way back into later pages, a URL Redirect action with “Fields To Pass” carries the result, and a Hidden Value action stores it.
Option B — On your page
Injection point: add a JavaScript Action to the verification page (Alchemer supplies the <script> tags; control when it runs via the Action's Logic tab).
<div id="vh"></div>
<script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"></script>
<script>
var done = false;
// Set to true ONLY after adding a branch on the verdict that screens out
// fail / score_blocked (e.g. End of Survey). Until then a failed respondent
// is held here with a message instead of continuing.
var RELEASE_ON_FAIL = false;
function released(verdict) {
return verdict === "pass" || verdict === "scored" || verdict === "review" ||
verdict === "error" || RELEASE_ON_FAIL;
}
var HELD_MESSAGE = "<p>This verification did not pass, so the survey cannot continue.</p>";
// Hide Alchemer's Next button until the check finishes
var next = document.querySelector("#sg_NextButton");
if (next) next.style.display = "none";
function finish(verdict) {
if (done) return;
done = true;
// write into a Hidden Value field
var hv = document.querySelector('input[name="vh_verdict"]');
if (hv) hv.value = verdict;
if (released(verdict)) { if (next) next.style.display = ""; }
else document.querySelector("#vh").insertAdjacentHTML("beforeend", HELD_MESSAGE);
}
VH.mount("#vh", {
siteKey: "<projectId>",
onResult: function (r) { finish(r.status); },
}).catch(function (err) {
finish(err && (err.code === "E020" || err.code === "E021") ? "error" : "fail");
});
</script>Store the result: add a Hidden Value action named vh_verdict on the same page; the snippet writes the result into it.
Add your Alchemer survey domain to the project's Allowed Domains in the dashboard before going live.
Gotchas
#sg_NextButtonis Alchemer's Next-button id — hide it on load, reveal it only on pass.- jQuery is not guaranteed to be present — our SDK is vanilla JS, so don't rely on
$in the surrounding code. - For a server-side source of truth, add a Webhook action (it fires on page submit, must return HTTP 200, and Alchemer retries up to 10×), or use our own
verification.completedwebhook.
LimeSurvey
Recommended path: On your page — LimeSurvey is open-source and self-hostable, so you get full control and the camera works on your own HTTPS page. Survey link (verify first) also works and needs no code.
Prerequisites for the inline path: Global Settings → Security → Filter HTML for XSS = No (a single global toggle; trivial on a self-hosted instance, required so your custom <script> isn't stripped).
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = LimeSurvey, type = Verify first, then survey.
- Pass redirect URL = your survey URL; Fail redirect URL = a terminate page. Save → copy
https://vhuman.riwi.com/v/<linkId>. - Use the
v/<linkId>link as the study entry point. On return, prefill a hidden question via?QCODE=value&newtest=Y; on the way out, the End URL can carry{SAVEDID}/{TOKEN}/{PASSTHRU:*}.
Option B — On your page
Injection point: add the SDK at the question, theme (config.xml), or plugin level. The simplest is a question with HTML/JS source.
<div id="vh"></div>
<script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"></script>
<script>
var done = false;
// Set to true ONLY after adding a branch on the verdict that screens out
// fail / score_blocked (e.g. End of Survey). Until then a failed respondent
// is held here with a message instead of continuing.
var RELEASE_ON_FAIL = false;
function released(verdict) {
return verdict === "pass" || verdict === "scored" || verdict === "review" ||
verdict === "error" || RELEASE_ON_FAIL;
}
var HELD_MESSAGE = "<p>This verification did not pass, so the survey cannot continue.</p>";
// Hide LimeSurvey's Next/Submit button until the check finishes
var btn = document.querySelector("#movenextbtn, #movesubmitbtn");
if (btn) btn.style.display = "none";
function finish(verdict) {
if (done) return;
done = true;
// write into the hidden short-text answer (replace XXXX with the SGQA id)
var ans = document.querySelector("#answerXXXX");
if (ans) ans.value = verdict;
if (released(verdict)) { if (btn) btn.style.display = ""; }
else document.querySelector("#vh").insertAdjacentHTML("beforeend", HELD_MESSAGE);
}
VH.mount("#vh", {
siteKey: "<projectId>",
onResult: function (r) { finish(r.status); },
}).catch(function (err) {
finish(err && (err.code === "E020" || err.code === "E021") ? "error" : "fail");
});
</script>Store the result: a hidden short-text question; the snippet writes the status into its #answerXXXX input (substitute your question's id).
More robust gate (recommended): instead of hiding the button, add a hidden + mandatory equation question whose ExpressionScript validation requires the stored result to equal pass — LimeSurvey then refuses to advance until the result is present, even if the button is force-clicked.
Add your LimeSurvey domain to the project's Allowed Domains in the dashboard before going live.
Gotchas
- XSS filter must be off (Global Settings → Security) or LimeSurvey strips your
<script>. This is a global instance setting. #movenextbtn/#movesubmitbtnare the navigation button ids; the submit button only appears on the last page.- For a server-side source of truth, use the
afterSurveyCompleteplugin event (outbound webhook) or the RemoteControl 2 JSON-RPC API — or our ownverification.completedwebhook.
Sawtooth (Lighthouse Studio / Discover)
Recommended path: On your page — Sawtooth's Free Format question gives you full HTML/CSS/JS and the cleanest hard gate of any platform (the page simply won't submit while an error message is set). Survey link (verify first) also works and needs no code.
Prerequisites: none beyond using a Free Format question (or an inline <script>) on the verification page.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Sawtooth, type = Verify first, then survey.
- Pass redirect URL = your study link; Fail redirect URL = a terminate page. Save → copy
https://vhuman.riwi.com/v/<linkId>. - Use the
v/<linkId>link as the study entry point. Inbound respondent IDs are captured with Pass-In Fields; the Terminate question links out (also your panel-router hook if the study is Cint/Dynata-fed).
Option B — On your page
Injection point: a Free Format question. Load the SDK from our CDN (or via <script src="[%GraphicsPath()%]verifyhuman.umd.js"> if you host it locally), start it, and use Custom JavaScript Verification to gate the page.
<div id="vh"></div>
<input type="hidden" name="FF1_token" id="FF1_token" />
<script src="https://vhuman.riwi.com/sdk/v1/verifyhuman.umd.js"></script>
<script>
var done = false;
// Set to true ONLY after adding a branch on the verdict that screens out
// fail / score_blocked (e.g. End of Survey). Until then a failed respondent
// is held here with a message instead of continuing.
var RELEASE_ON_FAIL = false;
function released(verdict) {
return verdict === "pass" || verdict === "scored" || verdict === "review" ||
verdict === "error" || RELEASE_ON_FAIL;
}
var HELD_MESSAGE = "<p>This verification did not pass, so the survey cannot continue.</p>";
window.__vhReleased = false;
function finish(verdict) {
if (done) return;
done = true;
document.getElementById("FF1_token").value = verdict; // store in Free Format var
if (released(verdict)) window.__vhReleased = true;
else document.querySelector("#vh").insertAdjacentHTML("beforeend", HELD_MESSAGE);
}
VH.mount("#vh", {
siteKey: "<projectId>",
onResult: function (r) { finish(r.status); },
}).catch(function (err) {
finish(err && (err.code === "E020" || err.code === "E021") ? "error" : "fail");
});
</script>
<!-- In the question's Custom JavaScript Verification box: -->
<!-- if (!window.__vhReleased) strErrorMessage = "Please complete verification."; -->Gate the page — in the question's Custom JavaScript Verification box:
if (!window.__vhReleased) strErrorMessage = "Please complete verification.";While strErrorMessage is a non-empty string, Sawtooth refuses to submit the page — the gate is enforced server-side by the survey engine, not just hidden in the DOM.
Store the result: a Free Format hidden variable (e.g. FF1_token), written by the snippet above.
Add your Sawtooth survey domain to the project's Allowed Domains in the dashboard before going live.
Gotchas
strErrorMessageis the gate — set it to a non-empty string to block submission, clear it (once__vhReleased = true) to allow it, then route on the stored result. This is more robust than hiding a Next button.- Sawtooth has no first-class submission webhook, so for a server-side source of truth lean on the redirect result (the signed
vh_token) or our ownverification.completedwebhook rather than a Sawtooth-fired one.
SurveyMonkey
Recommended path: Survey link (verify first). SurveyMonkey allows no custom JavaScript on any tier (Custom CSS is Enterprise-only and can't load scripts), so an on-your-page snippet is not possible — you must verify on a VerifyHuman-hosted page first, then hand respondents into the survey.
Prerequisite: use a Web Link collector for the survey. The End-Page redirect (needed to send fails to a disqualification URL) is not available on the Website (embed) collector.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = SurveyMonkey, type = Verify first, then survey.
- Pass redirect URL = your survey's Web Link collector URL. Fail redirect URL = a disqualification page. Save → copy
https://vhuman.riwi.com/v/<linkId>. - Distribute the
v/<linkId>link as your study entry point instead of the raw SurveyMonkey link. - Read the result back: set the Pass redirect URL to your Web Link collector with
?vh_verdict=passadded (we append?vh_token=...). SurveyMonkey storesvh_verdictas a Custom Variable in the results alongside the responses (like a hidden question). On fail, the link instead sends the respondent to your disqualification URL.
Option B — On your page
Not possible on SurveyMonkey. There is no custom-JavaScript slot on any plan (Enterprise Custom CSS cannot run logic or load scripts), and the camera can't run inside the embed collector's frame. Use Option A.
Optional — confirm the result on your server (for developers)
You can skip this. Option A already returns the result. This section is only for teams that want their own server-side check.
Subscribe a webhook to response_completed. The payload is just a notification — fetch the full response (and its custom variables, including vh_verdict) via the REST API v3, or simply rely on VerifyHuman's own verification.completed webhook, which carries the signed result directly.
Gotchas
- Web Link collector only. End-Page redirects don't fire on the Website / embed collector — fails wouldn't be routed.
- Custom Variables are the only place the result can land in SurveyMonkey results; confirm your plan exposes the Custom Variables UI before launch.
- The signed
vh_token(also appended on the pass redirect) is the tamper-proof proof — the in-surveyvh_verdictcustom variable is convenience only.
Typeform
Recommended path: Survey link (verify first). The Typeform form runs inside a locked, cross-origin iframe and accepts no custom code (explicitly off-roadmap), so an on-your-page snippet is not possible — the camera can't run in that frame. Verify on a VerifyHuman-hosted page first, then hand respondents in.
Prerequisite: the verify-after variant (redirect on completion) requires the Plus plan or above. The verify-first variant works on any plan.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Typeform, type = Verify first, then survey.
- Pass redirect URL = your Typeform link with the result mapped to a Hidden Field, e.g.
https://yourname.typeform.com/to/AbC123#vh_verdict=pass. Fail redirect URL = a disqualification page. Save → copyhttps://vhuman.riwi.com/v/<linkId>. - In Typeform, add a Hidden Field named
vh_verdict(Create → Hidden Fields). Hidden-field values are fixed before the form starts, which is exactly right for an incoming result. - Distribute the
v/<linkId>link as your study entry point.
(Verify-after alternative, Plus+): set the integration type to Survey first, then verify and use Typeform's redirect-on-completion with Recall params to send finished respondents to the v/<linkId> page.
Option B — On your page
Not possible on Typeform. The form is a sandboxed iframe with no custom-code slot, so the SDK can't run inside it and the camera won't run there. Use Option A.
Optional — confirm the result on your server (for developers)
You can skip this. Option A already returns the result. This section is only for teams that want their own server-side check.
Subscribe the Webhooks API: on submit, Typeform posts a rich form_response payload (form token, variables, hidden fields including vh_verdict, and answers). The Responses API mirrors the same data. Or rely on VerifyHuman's own verification.completed webhook with the signed result.
Gotchas
- Hidden Fields vs. Variables: Hidden Fields are set before the form starts (use these for the incoming result). Variables are for values computed during the form — not what you want here.
- Redirect-on-completion is Plus+. Verify-first needs no special tier; only the verify-after flow does.
- The signed
vh_tokenon the pass redirect is the tamper-proof proof; thevh_verdicthidden field is convenience only.
Jotform
Recommended path: Survey link (verify first). Jotform strips <script> from hosted forms (you only get the Paragraph-widget HTML and per-widget Custom CSS), and its widget iframes are sandboxed with no camera grant — so an on-your-page snippet that hosts the webcam is not possible. Verify on a VerifyHuman-hosted page first, then hand respondents in.
Prerequisite: none for the verify-first flow.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Jotform, type = Verify first, then survey.
- Pass redirect URL = your Jotform link (optionally with prefill parameters that populate hidden fields, e.g.
?vh_verdict=pass). Fail redirect URL = a disqualification page. Save → copyhttps://vhuman.riwi.com/v/<linkId>. - In Jotform, add a hidden/short-text field
vh_verdict; the URL prefill parameter populates it on entry. - Distribute the
v/<linkId>link as your study entry point.
(Verify-after alternative:) set type = Survey first, then verify and use Settings → Thank-You Page → Redirect to an external link (with field placeholders) to send finished respondents to the v/<linkId> page. A Redirect with HTTP POST can also ship the whole submission to an endpoint.
Option B — On your page
Not possible on Jotform. Hosted forms strip custom <script> tags, and widget iframes are sandboxed without a camera grant — a framed “VerifyHuman” widget could only launch (pop out / redirect) to the real check, not host it. Use Option A.
Optional — confirm the result on your server (for developers)
You can skip this. Option A already returns the result. This section is only for teams that want their own server-side check.
Jotform supports native outbound webhooks and a REST API; subscribe a webhook to read the submission (with the vh_verdict field). Or rely on VerifyHuman's own verification.completed webhook with the signed result.
Gotchas
<script>is stripped on hosted forms — there is no real inline-JS slot, so don't try to run the SDK in a Paragraph widget.- Widget iframes have no camera permission — even a published Jotform widget would have to redirect/pop out for the webcam step.
- The signed
vh_tokenon the pass redirect is the tamper-proof proof; the prefilledvh_verdictfield is convenience only.
Cint (incl. Lucid heritage)
Recommended path: Panel router (verify-first screener). Cint is a panel/sample router with no custom JavaScript — but its entry-link → exit-redirect model maps onto VerifyHuman's redirect mode almost exactly. Insert the VerifyHuman-hosted page as a pre-survey screener at study start: passes continue to the real survey; fails route straight to Cint's terminate URL with the right disposition.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Cint, type = Verify first, then survey.
- Pick the Cint panel preset and fill it in:
- Respondent-id param:
rid(Cint substitutes the live value via[%RID%]on the entry link). - Pass / continue URL: your real survey's entry URL.
- Screenout URL, Quota-full URL, Security-terminate URL: the Cint exit links for each disposition (below). The dashboard echoes
ridonto each automatically.
- Respondent-id param:
- Add
ridto passthrough params so the respondent id threads through to the redirect and the webhook. - Save → copy
https://vhuman.riwi.com/v/<linkId>. Set thisv/<linkId>link as the study entry link in Cint (with?rid=[%RID%]appended).
Option B — On your page
Not possible. Cint is a router with no place for JavaScript. Use Option A.
Disposition → outcome mapping
VerifyHuman routes each outcome to the matching Cint disposition code:
| VerifyHuman outcome | Cint disposition | Code |
|---|---|---|
| Pass (verified human) | Continue to survey | — (continue) |
| Fail (high risk, or not a live person) | Quality Terminate + security marker | 4 |
| Survey-side complete | Complete | 5 |
| Survey-side screenout | Screenout | 2 |
| Survey-side quota full | Quota Full | 3 |
For a VerifyHuman fail, terminate with the security marker markers="CINT_Exchange_Security" so the rejection is flagged as a security termination, not an ordinary screenout.
Entry / exit URL shapes
Entry link (set in Cint, points at the VerifyHuman page):
https://vhuman.riwi.com/v/<linkId>?rid=[%RID%][%RID%] is Cint's respondent-id substitution. The full entry URL must be ≤ 1999 characters; an optional SHA-1 signature, if used, must be the last parameter.
Exit / return (VerifyHuman sends the respondent back to Cint with the outcome):
https://<cint-host>/ClientCallBack.aspx?RID=<rid>&... (+ S2S Respondent Outcomes API)Gotchas
- Wait for the S2S 'success' response before redirecting. Cint's server-to-server Respondent Outcomes call must confirm before the browser redirect fires, or the disposition can be lost.
- Some Cint protection terminations (131 / 301 / 302 / 303) don't redirect — handle them as terminal, not as a continue.
- The security marker (
CINT_Exchange_Security) is what distinguishes a security termination from a normal screenout — set it on VerifyHuman fails.
Dynata
Recommended path: Panel router (verify-first screener). Dynata is a panel/sample router with no custom JavaScript — but its entry-link → exit-redirect model maps onto VerifyHuman's redirect mode. Insert the VerifyHuman-hosted page as a pre-survey screener at study start: passes continue to the real survey; fails route to Dynata's screenout/terminate URL, echoing the respondent id.
Dynata has two entry styles — legacy Samplify (psid passthrough) and REX (signed entry link). Both are supported.
Option A — Survey link (no code)
- Dashboard: Project → Survey integrations → Create link. Platform = Dynata, type = Verify first, then survey.
- Pick the Dynata panel preset and fill it in:
- Respondent-id param:
psid(Samplify) — VerifyHuman echoes it on every exit URL. For REX, the entry link is signed (see below). - Pass / continue URL: your real survey's entry URL.
- Complete URL, Screenout URL, Overquota URL: the three Dynata exit links; the dashboard echoes
psidonto each.
- Respondent-id param:
- Add
psidto passthrough params so the respondent id threads through to the redirect and the webhook. - Save → copy
https://vhuman.riwi.com/v/<linkId>. Set thisv/<linkId>link as the study entry link in Dynata.
Option B — On your page
Not possible. Dynata is a router with no place for JavaScript. Use Option A.
Disposition → outcome mapping
| VerifyHuman outcome | Dynata exit |
|---|---|
| Pass (verified human) | Continue to survey |
| Fail (high risk, or not a live person) | Screenout / Terminate (echo psid) |
| Survey-side complete | Complete |
| Survey-side quota full | Overquota |
| Survey-side screenout | Screenout |
On a VerifyHuman fail, redirect to the Screenout/Terminate exit URL and echo the respondent id so Dynata reconciles the disposition.
Entry / exit URL shapes
Legacy Samplify — psid passthrough:
Entry: https://vhuman.riwi.com/v/<linkId>?psid=<dynata-psid>
Exit: https://<dynata-host>/complete?psid=<psid> (Complete)
https://<dynata-host>/screenout?psid=<psid> (Screenout)
https://<dynata-host>/overquota?psid=<psid> (Overquota)REX — signed entry link + signed outbound redirect:
Entry: ...?respondent_id=<id>&expiration=<ts>&access_key=<key>&signature=<sig>
Exit: ...?disposition=<code>&status=<status>&signature=<sig>Gotchas
- Echo
psidon every exit URL (Samplify) — Dynata reconciles the respondent by it; a missingpsiddrops the disposition. - REX entry links are signed (
respondent_id,expiration,access_key,signature) and the outbound redirect must carrydisposition+status+signature— use the REX preset, not the Samplify one, for REX studies. - The signed
vh_tokenon the pass redirect is VerifyHuman's tamper-proof proof, independent of Dynata's own disposition reconciliation.
Next steps
- Building your own on-page integration? See the SDK guide for every
VH.mountoption and the result shape. - Verifying the signed
vh_tokenserver-side? See the Server API reference and the webhook contract. - Already a customer? Your project's Survey integrations tab pre-fills these recipes with your real Project ID and link ids — sign in to the dashboard.