Decode answer values
For backend or app engineers who receive funnel answers as option IDs and want meaningful values.
You'll have a tiny lookup — in any language — that turns interests=3,2 into ["Finance", "Wellness"].
You need the Variables Schema exported from your funnel (one click in the dashboard).
Why answers arrive as IDs#
When a visitor answers a choice question (single-select, multi-select, rating, slider), Zellify stores their pick as a short, stable option ID — "2", or ["3","2","4"] for multi-select — not the text they saw on screen. IDs are compact, never change when you edit option wording, and are safe to forward in a URL.
That trade-off is why you sometimes see interests=3,2 instead of words. The mapping from ID to meaning lives in the Variables Schema.
There are two places you'll receive these answers, and they differ in one important way:
| Source | Shape | Already has the label? |
|---|---|---|
Registration webhook answers | { "value": "2", "label": "1-5 years" } per question | Yes — read label directly |
| Deep Link Button URL parameters | ?relationship_length=2&interests=3,2 (IDs only) | No — you must decode |
label next to each value. The Deep Link Button forwards only IDs (URLs stay short), so decoding is required there — and it's also how you normalize answers to your own canonical values rather than Zellify's display text.Step 1 — Export the Variables Schema#
The schema is a JSON array describing every question in your funnel: its name (the key you receive answers under), its questionType, and — for choice questions — the full list of options with id and label.
- Open Dashboard → Funnels
- Click Edit Funnel
- Open the Variables tab in the right sidebar
- Click Copy Variables Schema
- Save it next to your handler (e.g.
variables-schema.json) and commit it
1[2 {3 "name": "primary_goal",4 "questionType": "singleselect",5 "questionText": "What's your main goal?",6 "options": [7 { "id": "1", "label": "Build a habit" },8 { "id": "2", "label": "Improve focus" },9 { "id": "3", "label": "Reduce stress" }10 ]11 },12 {13 "name": "relationship_length",14 "questionType": "singleselect",15 "questionText": "How long have you been together?",16 "options": [17 { "id": "1", "label": "Less than a year" },18 { "id": "2", "label": "1-5 years" },19 { "id": "3", "label": "6-10 years" }20 ]21 },22 {23 "name": "interests",24 "questionType": "multiselect",25 "questionText": "What are you interested in?",26 "options": [27 { "id": "1", "label": "Productivity" },28 { "id": "2", "label": "Wellness" },29 { "id": "3", "label": "Finance" },30 { "id": "4", "label": "Fitness" }31 ]32 }33]
The full field reference (including range and image/icon options) is in the Payload reference.
Step 2 — Decode, in three steps#
The logic is the same in every language:
- Index the schema by question
nameso you can find a question in O(1). - For a choice question, build an
id → labelmap from itsoptions. - For each incoming answer, split multi-values (an array, or a comma-joined string), then map each ID through that lookup, falling back to the raw ID if it's not found.
Questions with no options (email, text, number, range) have nothing to map — pass them through unchanged.
id → ... map at whatever your system uses — Zellify's display label, your own enum, a slug, a database key. The lookup doesn't care what the target is.How each question type decodes#
questionType | Raw value you receive | Decode to |
|---|---|---|
email / text | the string | itself — no options |
number | the number | itself |
singleselect / slider | one option ID, e.g. "2" | options[id].label |
multiselect | IDs — array ["3","2"] (webhook) or comma string "3,2" (deep link) | list of labels |
range | "from-to", e.g. "18-35" | split on - for the bounds |
| consent / terms | "1" or "0" | options[id].label → e.g. "Accepted" / "Declined" |
Worked example#
A Deep Link Button on your success page forwards three answers:
?primary_goal=1&relationship_length=2&interests=3,2
Decoded against the schema above:
| Parameter | Raw | Decoded |
|---|---|---|
primary_goal | 1 | Build a habit |
relationship_length | 2 | 1-5 years |
interests | 3,2 | ["Finance", "Wellness"] |
Gotchas#
- Multi-select arrives differently per source. The webhook gives an array (
["3","2"]); the Deep Link Button gives a comma-joined string ("3,2"). The snippets above handle both. - Some options have no text label. Rating scales and image pickers can ship options with
label: ""plus animageoriconfield — the meaning is the picture the user saw, not text. There, the ID itself (e.g."5"on a 1–7 scale) is usually the value you want; map it to your own wording if you need words. - Branching funnels → most keys are absent. If your funnel splits into paths, the schema lists every question across all paths, but a given visitor only answers the ones on their path. Treat a missing key as "not asked" — don't assume every schema entry shows up.
- Don't split free text on commas. Decode by
questionType(as the snippets do), not by blindly splitting every value — atextanswer like"Doe, Jane"contains a comma but isn't a multi-value. - IDs are stable; the schema is a snapshot. Deleting an option never renumbers the others, and new options get the next free ID — so keying on IDs is safe. But labels and the set of questions change when you edit the funnel. Re-export and re-commit the schema whenever the funnel changes, and diff it like any generated artifact.