When a customer takes the hair quiz, the Concierge remembers their answers and uses them to give personal recommendations.
The AI Hair Test on the KIWABI site (built by Tangent).
Their answers, plus who they are (their email, if shared).
Live in the same visit, or saved for a return visit.
Right products, in-context answers, a smoother sale.
on the KIWABI site. That quiz belongs to Tangent.
their answers (hair type, concerns) and who they are (their email, if shared).
so it is not starting from scratch, it already knows this person's hair profile.
it greets them by their results, recommends the right products, and answers in context.
quiz interest becomes a guided, personal conversation that is far more likely to end in a sale.
The quiz and the Concierge talk to each other right on the page. The moment someone finishes the quiz, the Concierge picks it up. Light build, no plumbing.
We also save each result, so a customer who took the quiz last week is recognized when they return, and the team can see the data.
Ideally the customer's email, so the answers match the right person.
So the Concierge knows the right moment to step in.
Live on the page, or delivered to us behind the scenes.
The customer agreed to this use, KIWABI owns the data, Tangent holds it for KIWABI.
We only ever read the data, only the fields the customer agreed to share, kept to the minimum needed — and KIWABI stays the owner of it the whole way through.
Two integration paths. The client-side bridge ships first; the webhook adds durability and returning-visitor lookups.
Same-visit personalization needs no identifier (the bridge hands off in the browser). Recognizing a returning visitor needs a stable, cross-session id: email ideally, or a persistent first-party id both sides can read on the page. A per-visit session id does not match across visits. Anonymous submissions are analytics-only.
Both widgets share the page. On quiz completion, hand the result straight to the Concierge. Ask Tangent how completion is signaled: a window event, an iframe postMessage, a completion callback, or a dataLayer push. Bridge data is a same-visit hint (user-forgeable); the signed webhook is the source of truth for anything stored.
// custom-event example
window.addEventListener('tangent:quiz_complete', (e) => {
const r = e.detail || {};
window.KiwabiConcierge?.setContext({
source: 'ai_hair_test',
submissionId: r.submissionId,
email: r.email || null,
profile: r.profile || r.result, // hair type, concerns, segment
recommendations: r.recommendations, // products shown
takenAt: r.completedAt
});
});
// iframe variant — verify origin first
window.addEventListener('message', (e) => {
if (e.origin !== 'https://TANGENT-DOMAIN') return;
if (e.data?.type === 'quiz_complete') { /* same handoff with e.data.payload */ }
});
X-Tangent-Signature: sha256=<hex>. TLS only. Fallback: bearer token.Payload we ask Tangent to send:
{
"submission_id": "tan_9f2...", // unique, for idempotency
"occurred_at": "2026-07-08T17:05:00Z",
"quiz": { "id": "ai_hair_test", "version": "3" },
"identity": {
"email": "jane@example.com", // the join key, if consented
"shopify_customer_id": null,
"session_id": "sess_abc", // correlate to the on-site session
"visitor_id": "vis_123"
},
"answers": [
{ "id": "hair_type", "q": "Hair type?", "a": "wavy" },
{ "id": "concern", "q": "Main concern?", "a": "thinning" }
],
"derived": { "segment": "thinning-wavy", "recommendations": ["Root Vanish"] },
"consent": { "marketing": true, "at": "2026-07-08T17:04:40Z" },
"market": "US", "locale": "en-US"
}
Receiver rules: verify the signature (401 if bad) · upsert by submission_id so retries are safe · store keyed by identity · return 200 fast and process async · Tangent retries any non-2xx with backoff.
@app.post("/api/quiz/tangent")
async def tangent_quiz(request):
raw = await request.body()
if not hmac_ok(raw, request.headers.get("X-Tangent-Signature"), SECRET):
return JSONResponse({"error": "bad signature"}, 401)
upsert_quiz_submission(json.loads(raw)) # idempotent on submission_id
return {"ok": True}
On a Concierge session, resolve identity (Shopify login email, or the on-page session/visitor id), look up the latest submission, and inject a compact profile into context: "AI Hair Test: wavy, concern = thinning, segment = thinning-wavy, shown Root Vanish." The Concierge personalizes from stored facts only. It never invents a hair profile, and if none is on file it simply asks.
KIWABI is the data owner and Tangent is the processor. The shared HMAC secret is generated on our side and handed over securely, never in email or chat.
Phase A stores nothing new. Phase B is gated on a short data-processing note, consent that covers Concierge personalization, and an agreed retention/deletion policy before any customer data flows.