distil

For product teams

Ask your users why, on the page where it happened.

Analytics tell you a user left. Paste one line and a chat bubble in the corner of your product asks them why, then follows up on what they say. The look (colour, logo, font) is set on the study, so the snippet never changes.

Free. Nothing is drawn on your page until distil confirms the study is open for that person.

What you get

Conversations are handled as the privacy page describes. distil may read them to improve the product; studies marked host-private drop that clause.

Developer reference

Embedding a distil study

Add one line to the page and a chat bubble appears in a corner. A person clicks it, talks with distil about your brief, and you read what they said at distil.cx. The snippet never changes once pasted; the look (colour, logo, font) lives on the study.

<script src="https://distil.cx/embed.js"
        data-distil="SHARE_TOKEN"
        data-launcher
        data-pid="USER_ID"
        async></script>

SHARE_TOKEN is the study’s share link token — the last part of its link. Nothing is drawn, and nothing on your page shifts, until distil confirms the study is open for this person.

Attributes

attribute meaning
data-distil required; the share token. Without data-launcher the conversation is framed inline where the tag sits (size it with data-width / data-height, default 480px × 640px).
data-launcher draw a corner button that opens the conversation in a panel (a full-screen sheet at 600px wide or narrower, or 560px high or shorter).
data-pid your opaque user id, forwarded to the study so you can join responses to your users.
data-context-<key> what the person just did, e.g. data-context-level="Year 9".
data-position bottom-right (default) or bottom-left; the panel opens from the same side.
data-offset-x, data-offset-y distance in px from the side and from the bottom; whole numbers, default 20, clamped to 0–400. Use them to clear your own help bubble.
data-hidden with data-launcher: load without drawing the bubble, so you open it yourself with Distil.open().

pid and context

The pid is stored only if it is at most 64 characters of A-Za-z0-9_-; anything else is dropped. Send an internal id — never a name or an email, and the charset rejects both. It identifies your user; it does not prove who they are.

Each data-context-<key> is kept if the key (hyphens become _) is 1–24 of a-z0-9_ — at most 6 keys — and the value is 1–40 characters of letters, digits, space and _-,./&+. Anything else is dropped. Both appear beside each response in distil and in distil conversations --json. The interviewer does not read them.

JavaScript API

window.Distil exists as soon as the script runs, even when nothing is drawn.

Distil.open();    // open the panel
Distil.close();   // close it
Distil.dismiss(); // remove the launcher and remember that, as the visitor's dismiss button does

open() works after a dismissal, because you asked explicitly. It does nothing when the study is not open or this pid has finished, and it waits for the check if that is still pending.

Events fire on document: distil:ready (detail.live is true or false), distil:open, distil:close, distil:dismiss, distil:done.

document.addEventListener("distil:done", () => analytics.track("study_done"));

When the launcher is not shown

  • Dismissed. Your page called Distil.dismiss(). We store distil:dismissed:<token>:<pid> in your site’s sessionStorage, so the bubble returns on the next visit.
  • Done. The visitor finished the conversation. The panel stays open on the thank-you screen and the launcher goes when they close it; we store distil:done:<token>:<pid>.
  • Closed. The study is at its response cap or past its end date, or this pid already finished.
  • Unreachable. distil.cx did not answer within about 8 seconds, or errored.

In each case your page is left exactly as it was. If localStorage is blocked, the launcher still works but forgets dismissals between visits. The launcher never throws on your page. Your browser’s network log may show a 404 for the check when a study is closed or finished; that is the signal, not a fault.

Accessibility

Escape closes the panel. Focus moves into the conversation on open and back to the bubble on close. The bubble announces its open state; the mobile sheet is a modal dialog and respects safe-area insets.

Content Security Policy

If you set a CSP, allow:

script-src https://distil.cx;
img-src    https://api.distil.cx;
frame-src  https://distil.cx;

The launcher loads its icon and its open/closed check as images from api.distil.cx. The conversation is a frame of https://distil.cx/e/<token>; it uses the microphone for voice answers, which the frame requests through its allow attribute (microphone; clipboard-write) — your own Permissions-Policy must not block the microphone for it. distil makes no cross-origin requests from your page, so you need no CORS configuration on either side.