Add voice responses to a Sawtooth Discover survey with one block of Custom JavaScript. The respondent speaks, the transcript is written into the question's own answer box, and Discover stores and exports it like any typed answer.
AvailableDiscover protects its surveys with a Content Security Policy, a list of the outside addresses a survey page may load scripts from and send data to. Anything not on that list is blocked by the browser before it starts, and Discover customers cannot edit the list themselves. In September 2026 Sawtooth added api.voicecapture.ai to it for Discover surveys, so there is normally nothing to do here. If the microphone never appears and the browser console reports that a request "violates the following Content Security Policy directive", send the text in this block to Sawtooth support and ask them to allow the domain. They apply the change on their side.
Please add https://api.voicecapture.ai to the Content Security Policy
of our Discover surveys:
script-src https://api.voicecapture.ai
connect-src https://api.voicecapture.aiIn your Discover survey go to Settings > Custom JavaScript and paste the block from your Voice Capture dashboard. In the QUESTIONS line, list the names of the open-ended questions that should get voice, exactly as they appear in the question editor (Q2, NPS_Why or whatever you called them). The script waits for each of those questions to be drawn, finds its text box and mounts the microphone above it. Custom JavaScript is a feature of Discover's paid plans.
// Voice Capture — paste into Settings > Custom JavaScript
(function () {
var PROJECT_KEY = 'proj_XXXXXXXX';
// names of the open-ended questions that get voice
var QUESTIONS = ['Q2'];
// One key per respondent for the whole survey. getRecordID (capital "ID")
// is Discover's own; the generated key is the fallback.
function respondentKey() {
try {
if (typeof getRecordID === 'function') {
var rid = getRecordID();
if (rid) return String(rid);
}
} catch (e) { /* fall back to a generated key */ }
var fresh = 'sd_' + Date.now() + '_' +
Math.random().toString(36).slice(2, 10);
try {
var stored = window.sessionStorage.getItem('gv_sid');
if (stored) return stored;
window.sessionStorage.setItem('gv_sid', fresh);
} catch (e) { /* storage blocked: per-page key */ }
return fresh;
}
var waiting = [];
function withLibrary(cb) {
if (window.GeniusVoice && window.GeniusVoice.init) return cb();
waiting.push(cb);
if (document.getElementById('gv-lib')) return;
var s = document.createElement('script');
s.id = 'gv-lib';
// the only host Discover's CSP allows
s.src = 'https://api.voicecapture.ai/voice.js';
s.onload = function () {
var q = waiting;
waiting = [];
q.forEach(function (f) { f(); });
};
document.body.appendChild(s);
}
// Discover sends the respondent to an error page if custom code throws.
function mount(name, el) {
try {
var root = el || document;
if (root.querySelector('[data-gv-host]')) return;
var field = root.querySelector('textarea') ||
root.querySelector('input[type="text"]');
if (!field) {
console.warn('[VoiceCapture] no text box in', name);
return;
}
if (!field.id) field.id = 'gv-field-' + name;
var sessionId = respondentKey();
var host = document.createElement('div');
host.setAttribute('data-gv-host', name);
host.setAttribute('data-project', PROJECT_KEY);
host.setAttribute('data-target', '#' + field.id);
host.setAttribute('data-session', sessionId);
host.setAttribute('data-question', name);
host.setAttribute('data-lang', 'es');
field.parentNode.insertBefore(host, field);
withLibrary(function () {
try {
window.GeniusVoice.init(host);
} catch (e) {
console.warn('[VoiceCapture]', e);
}
});
} catch (e) {
console.warn('[VoiceCapture]', e);
}
}
if (typeof addOnQuestionLoadHandler !== 'function') {
console.warn('[VoiceCapture] addOnQuestionLoadHandler is not available');
return;
}
QUESTIONS.forEach(function (name) {
try {
addOnQuestionLoadHandler(name, function (el) { mount(name, el); });
} catch (e) {
console.warn('[VoiceCapture] could not watch question', name, e);
}
});
})();Discover's Test button opens the survey with your custom code running, so you can see the microphone, record a few seconds and watch the transcript appear in the box before anything is published. Responses taken this way are kept apart as test data, which is useful: they never mix with real fieldwork. When you download data, check which of the two sets you are looking at, because a test response will not appear in the export of live responses.
This is the check that decides whether the setup works. Record an answer, move on and finish the survey, then open Analysis on the voice question: the transcript should be listed among its responses. Download the data as well and look at the question's column, where the transcript sits in the same row as the respondent's other answers. If it is there, the integration is doing its job, and the rest of your analysis does not need Voice Capture at all.
Publish and field the study as you normally would. Because the transcript is the answer, Discover treats it as one: we have confirmed it in the question's response list in Analysis and in the Excel export, in the same row as the respondent's other answers. Other Discover tools that read open ends, such as its AI summary or crosstabs, should see the same text, but we have not tested each of them. The Voice Capture dashboard keeps its own copy of each response, with whether it was spoken or typed, how long the recording was and the language, which is what you need if you want to compare spoken and typed answers.
The script tags every response with Discover's getRecordID value, so all the answers one respondent gives across your voice questions carry the same key in Voice Capture. That key is not the Record ID column of the Discover export, which is a different identifier, so it groups Voice Capture rows together rather than matching them to Discover rows. For most studies this does not matter, because the transcript already travels inside the Discover export.
Open the survey with the browser console visible. A message saying a request violates the Content Security Policy means Discover is blocking our domain: send the text from step 1 to Sawtooth support. If there is no such message, check that the names in QUESTIONS match the question names exactly, including upper and lower case.
Discover shows its error page when custom code throws an error. The Voice Capture block catches its own errors, so look for a change made to the block after pasting it, or for other custom code in the same box.
Check that the question is an open end and that its name is in QUESTIONS, then make sure you are reading the right data set: answers given through the Test button are stored as test data, separately from live responses.
This guide covers Discover, which is where we have tested Voice Capture end to end. Lighthouse Studio is a different Sawtooth product and we have not verified it yet. If you field in Lighthouse, write to us before you build the survey.
Our AI assistant can walk you through the setup step by step.
Set up Voice Capture in under 5 minutes. No credit card. No commitment.
Get Started Free