Skip to content

Redirect after a quiz finishes

Goal: send the shopper to a page of your choice as soon as they finish a quiz, for example a collection that matches their answers.

Uses: Storefront JS API. Nothing to install: the API is part of the quiz embed already on the page. Events fire on the Plus and Enterprise plans.

quiz.finished carries everything the quiz collected: the answers, the points per dimension, the products shown and the discount codes. Each answer names the question (component_key), the value as stored (value), the option ids picked for a choice (option_ids) and the labels the shopper saw (values). Put only choice answers in the URL: a text, email or phone answer is the shopper’s own words and must never land in an address that gets logged.

<script>
window.octaneai = window.octaneai || [];
window.octaneai.push(function (octaneai) {
octaneai.on('quiz.finished', function (event) {
var result = event.data.object;
var params = new URLSearchParams();
result.answers.forEach(function (answer) {
if (!answer.option_ids.length) return; // choice answers only, never free text
params.append(answer.component_key, answer.option_ids.join(','));
});
if (result.terminal_page) params.append('result', result.terminal_page);
window.location.href = '/collections/recommended?' + params.toString();
});
});
</script>

quiz.finished fires once, after the result page is shown, so the shopper lands on your page rather than staying on the quiz’s own result page.

Every snippet below runs inside the same window.octaneai.push(function (octaneai) { ... }) callback: before the quiz script loads, window.octaneai is a plain array and octaneai.on does not exist yet.

Read the answer to a specific question by its component key (the key the question has in the editor; a person’s latest_results[].answers[] on the REST API lists the same keys):

window.octaneai = window.octaneai || [];
window.octaneai.push(function (octaneai) {
octaneai.on('quiz.finished', function (event) {
var skinType = event.data.object.answers.find(function (answer) {
return answer.component_key === 'image_choice-48su5';
});
if (skinType && skinType.option_ids.indexOf('dry') !== -1) {
window.location.href = '/collections/dry-skin';
}
});
});

For a points quiz, points maps each dimension id to its total:

window.octaneai = window.octaneai || [];
window.octaneai.push(function (octaneai) {
octaneai.on('quiz.finished', function (event) {
var points = event.data.object.points;
var winner = Object.keys(points).sort(function (a, b) {
return points[b] - points[a];
})[0];
if (winner) window.location.href = '/collections/' + winner;
});
});

getState() returns what the quiz has so far: answers maps component keys to their values (saved or still on the page), page_key is the current page, and points and discount_codes are the running totals and the codes issued so far.

window.octaneai = window.octaneai || [];
window.octaneai.push(function (octaneai) {
octaneai.ready(function (api) {
api.on('page.viewed', function () {
var state = api.getState();
console.log('On page', state.page_key, 'with answers', state.answers);
});
});
});

Every answer shows up in question.answered as it is saved, so you can also react question by question:

window.octaneai = window.octaneai || [];
window.octaneai.push(function (octaneai) {
octaneai.on('question.answered', function (event) {
var answer = event.data.object;
console.log(answer.component_key, answer.value, answer.option_ids, answer.values);
});
});
  • Set localStorage['octaneai:debug'] = '1' in the console to see every event logged as it fires.
  • A listener that throws is logged as [OctaneAI] listener for quiz.finished threw and never breaks the quiz.
  • On a plan without the API, window.octaneai exists and on() registers, but no event fires and the console says so once.