Registration payload reference

Pure spec for the Registration webhook body. For overview, signature verification, and retry behaviour see Registration webhook.

Top-level fields#

FieldTypeRequiredDescription
emailstringyesEmail the visitor submitted at the registration step.
answersobjectyesMap of Component ID → answer object. See Answers and question types.
contextobjectyesSession metadata. See Context fields.

Context fields#

FieldTypeRequiredDescription
appUserIdstring (UUID)yesZellify funnel session identifier. Same value appears in Stripe, Paddle, and Solidgate metadata as app_user_id.
funnelIdstringyesFunnel where the registration happened.
campaignIdstringyesCampaign link used to reach the funnel.
experimentIdstring | nullyesExperiment id if the campaign targets one, else null.
experimentTagstring | nullyesAlias of experimentId — both fields carry the same value. Kept for backwards compatibility; prefer experimentId in new code.
organizationIdstring (UUID)yesYour Zellify organization id.
pageLocationstringyesPath where the registration event fired.
ipstringyesVisitor IP (v4 or v6).
userAgentstringyesVisitor user agent string.
sessionTokenstring | nullyesOpaque session continuity token used internally by Zellify's tracker. Treat as opaque; not typically needed for integrations.

Answers and question types#

answers is keyed by the Component ID set on each question component in the funnel builder (e.g. age, gender). Each entry has this shape:

Code
1type Answer<V> = {2  value: V                // V varies by question type — see table below3  label?: string          // chosen option's display text; for multiselect, a comma-joined string of selected labels4  pageSlug?: string       // slug of the page the question lives on5  questionText?: string   // the question's prompt as configured in the builder6}

Only value is guaranteed. The three metadata fields appear when populated by the question component (label requires options metadata; pageSlug requires the variable to be declared on a page; questionText requires the component author to set it). Plain email/text/number inputs typically only emit value and pageSlug.

Question typevalue shapeExample
emailstring{ "value": "[email protected]" }
textstring{ "value": "free text" }
numbernumber{ "value": 35 }
singleselectstring (option ID){ "value": "2", "label": "Female" }
multiselectstring[] (option IDs){ "value": ["1", "3"], "label": "Apple, Banana" }
sliderstring (option ID){ "value": "3", "label": "Often" }
rangestring ("from-to"){ "value": "18-35" }

Option IDs are sequential numeric strings ("1", "2", "3", …) auto-assigned by the builder. They're stable across edits — deleting an option doesn't renumber the others, and new options get the next available ID. For singleselect and slider, label carries the option's display text directly so you usually don't need to resolve IDs at all. For multiselect, label joins the chosen option labels with ", ". If you need an array of labels, a per-option mapping, or the question's full schema for type generation, export the Variables Schema below.

range answers serialize as a single string in "from-to" format (e.g. "18-35"); split on the hyphen to read the bounds.

Variables Schema#

The webhook payload carries raw user input. The Variables Schema gives you the metadata to interpret it — every question in the funnel, its type, its prompt, the page it lives on, and (for choice questions) the full set of options.

You'll want it any time you:

  • Generate TypeScript types or a JSON schema for the answer payload your backend sees
  • Render a per-option report (e.g. "5 users picked Apple, 12 picked Banana")
  • Validate or normalise answers server-side beyond what label already gives you
  • Build a dashboard, CRM, or analytics view that knows what every question was

How to export#

  1. Open Dashboard → Funnels
  2. Click Edit Funnel on the funnel you want
  3. Open the Variables tab in the right sidebar
  4. Click Copy Variables Schema (top-right of the panel)
  5. Paste into your backend repo, design doc, or schema-generation pipeline

The export is a JSON array — one entry per declared variable in the funnel.

Schema shape#

Each entry describes one question:

Code
1type SchemaVariable = {2  name: string                   // the Component ID — same key used in `answers`3  questionType:4    | "email" | "text" | "number"5    | "singleselect" | "multiselect"6    | "slider" | "range"7  questionText: string           // the question prompt as shown to the user8  pageSlug: string               // the page the question lives on, "" if unbound9  options?: Array<{              // present for singleselect, multiselect, slider10    id: string                   // matches the `value` you'll see in `answers`11    label?: string               // display text — same value the runtime puts in `label`12  }>13  range?: { from: number; to: number; step: number }  // present for range / numeric wheel pickers14}

Example#

variables-schema.json
1[2  {3    "name": "nickname",4    "questionType": "text",5    "questionText": "What is your nickname",6    "pageSlug": ""7  },8  {9    "name": "email",10    "questionType": "email",11    "questionText": "Enter your email to get your personal plan",12    "pageSlug": "page-2"13  },14  {15    "name": "age",16    "questionType": "number",17    "questionText": "What is your age",18    "pageSlug": ""19  },20  {21    "name": "gender",22    "questionType": "singleselect",23    "questionText": "What is your gender",24    "pageSlug": "",25    "options": [26      { "id": "1", "label": "Male" },27      { "id": "2", "label": "Female" }28    ]29  },30  {31    "name": "favoriteActivities",32    "questionType": "multiselect",33    "questionText": "What is your favorite activity",34    "pageSlug": "",35    "options": [36      { "id": "1", "label": "Play" },37      { "id": "2", "label": "Dance" },38      { "id": "3", "label": "Paint" },39      { "id": "4", "label": "Walk" }40    ]41  }42]

The name of each schema entry matches the answers key in the webhook payload, and each option's id matches what you'll see as value (or as an entry inside value for multiselect). That's the contract — schema and payload key off the same identifiers.

Schema is a snapshot, not a live link: The exported JSON reflects the funnel's variables at the moment you click Copy. Re-export after editing questions, adding options, or changing Component IDs. Treat it like a generated artifact: check it into version control alongside your webhook handler, regenerate when the funnel changes, and diff to see what shifted.

When you don't need it#

If your handler only needs app_user_id plus a few well-known fields (email, age, etc.), and you're happy reading label directly off the payload for choice questions, you can skip the schema. The runtime payload is self-describing for the common cases — schema export becomes important when you need the full picture (every question, every option, every type) ahead of time.

Handling missing fields#

Every required field in the tables above is always present on a valid, signature-verified payload. Treat a missing required field as a sign the body was tampered with or the request is not from Zellify, and reject with 400.

The optional answer fields (label, pageSlug, questionText) may legitimately be absent — handle the missing case rather than assuming they're present.

Registration webhook
Overview, signature, retries, delivery logs.
Funnel variables
Where Component IDs come from in the builder.
app_user_id
How `context.appUserId` links downstream.
Early account creation
Pair this webhook with payment events.