Skip to content

Put the quiz result on the order

Goal: an order a quiz taker places from the same cart carries their result, so support, fulfilment and Shopify Flow can act on it (a packing insert per routine, a tag per skin type).

Uses: Storefront JS API and Shopify’s cart attributes. Nothing to install on the quiz side; no API key.

Shopify’s Ajax cart API stores free-form attributes on the cart, and they travel to the order. On quiz.finished, write the result page, the session id and the choice answers only (a text, email or phone answer is the shopper’s own words and never belongs on an order attribute that every staff account reads):

<script>
window.octaneai = window.octaneai || [];
window.octaneai.push(function (api) {
api.on('quiz.finished', function (event) {
var o = event.data.object;
var answers = o.answers
.filter(function (a) { return a.option_ids.length > 0; }) // choice answers only, never free text
.slice(0, 20) // keep the attribute small; never cut the JSON itself
.map(function (a) { return [a.component_key, a.values.join(', ')]; });
fetch('/cart/update.js', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
attributes: {
quiz_result: o.terminal_page || '',
quiz_session: o.session_id || '',
quiz_answers: JSON.stringify(answers)
}
})
});
});
});
</script>
  • terminal_page is the key of the result page the shopper reached; use it as the coarse “which routine” value.
  • session_id is the sess_ id the REST API and the webhooks name, so an order can be traced back to the person (GET /v1/profiles/{profile_id} lists orders[] with the session_id that earned each one).
  • values are the labels the shopper saw; value is the stored id. Labels read better on a packing slip.
  • Order: the attributes appear as the order’s additional details (order.customAttributes in the Admin API, note_attributes on the REST order).
  • Shopify Flow: trigger Order created, condition order.customAttributes contains a quiz_result value, then Add order tags or Send internal email for the packing insert.
  • Checkout UI extension: useAttributeValues(['quiz_result']) reads the attribute to show “Picked for your dry-skin routine” on the checkout.
  • Keep the payload small: twenty choice answers is plenty for a packing slip. The cart write is asynchronous and unchecked, so a shopper who checks out within the same second may miss it.
  • Pick keys of your own. The quiz embed writes cart attributes prefixed __octane_ (__octane_session_id, __octane_app_id, __octane_page_key) and line-item properties prefixed _octane_ for order attribution; do not reuse or overwrite them.
  • Accelerated checkouts (Buy now, Shop Pay from a product page) start a fresh checkout and may not carry cart attributes.
  • quiz.finished fires once per session; a shopper who retakes the quiz starts a new session and the handler runs again, overwriting the attributes with the new result.