JSON Builder Reference

Complete reference for constructing Clarflow quiz funnel JSON. Build funnels with AI tools and paste directly into the Clarflow Builder.

Clarflow JSON Builder Reference

Build complete quiz funnels with any AI tool and paste them directly into the Clarflow Builder canvas. This document is your complete reference for the JSON format.


How It Works

  1. Copy this document — click the "Copy Full Markdown" button at the top of this page
  2. Paste into your AI tool — Claude Code, ChatGPT, or any AI assistant
  3. Describe your funnel — e.g. "Build me a 10-step quiz funnel for a collagen supplement targeting women 35+"
  4. Get JSON output — the AI generates valid Clarflow clipboard JSON
  5. Paste into the Builder — open your funnel in Clarflow, click on the canvas, and press Ctrl/Cmd + V

The AI uses this reference to produce JSON that the Clarflow Builder accepts natively via clipboard paste. No manual editing needed.


Clipboard Payload Format

When pasting JSON onto the Clarflow canvas, the clipboard must contain a JSON string with this exact structure:

{
  "type": "clarflow-canvas-nodes",
  "nodes": [ ... ],
  "edges": [ ... ],
  "timestamp": 1710400000000
}
FieldTypeRequiredDescription
typestringYesMust be exactly "clarflow-canvas-nodes". Validation key.
nodesNode[]YesArray of node objects.
edgesEdge[]YesArray of edge objects. Can be empty [].
timestampnumberYesUnix timestamp in milliseconds (e.g. Date.now()).

The type field is a discriminator. If it doesn't match exactly, the paste is silently ignored.


Node Types Overview

Clarflow has 4 node types. Each node in the nodes array follows this base structure:

{
  "id": "step_1710400000000_abc123def",
  "type": "quizStep",
  "position": { "x": 100, "y": 200 },
  "data": { ... },
  "width": 280,
  "height": 200
}
FieldTypeRequiredDescription
idstringYesUnique node ID. See ID Conventions section.
typestringYesOne of: quizStep, exitNode, ifElseNode, startNode.
position{ x, y }YesCanvas coordinates (top-left of the node).
dataobjectYesNode-specific data. Structure varies by type.
widthnumberYesNode width in pixels. Default: 280.
heightnumberYesNode height in pixels. Default: 200. Doubled for A/B tests.
Node TypeDescriptionCan Be Pasted?
quizStepContent step with sub-elements (questions, text, images, etc.)Yes
exitNodeTerminal node that redirects to a URLYes
ifElseNodeConditional branching nodeYes
startNodeEntry point of the funnelNo (auto-excluded on copy)

Step Node (quizStep)

The primary content node. Contains sub-elements that the end-user sees.

{
  "id": "step_1710400000000_abc123def",
  "type": "quizStep",
  "position": { "x": 100, "y": 200 },
  "data": {
    "name": "Step 1",
    "subEls": [ ... ],
    "size": { "w": 280, "h": 200 },
    "buttonText": "Continue",
    "hideContinueButton": false,
    "isConditional": false,
    "conditionalQuestionIndex": 0,
    "isABTest": false,
    "variantA": null,
    "variantB": null,
    "variantAWeight": 50
  },
  "width": 280,
  "height": 200
}
FieldTypeRequiredDefaultDescription
namestringNo"New Step"Internal label in the canvas builder. Not visible to end-users.
subElsSubElement[]Yes[]Array of content blocks. See Sub-Elements section.
size{ w, h }Yes{ w: 280, h: 200 }Internal size. w = width, h = height in pixels.
buttonTextstringNo"Continue"Custom label for the continue button shown to end-users.
hideContinueButtonbooleanNoundefinedWhen true, removes the continue button from this step. Intended for steps holding a Shopify Add to Cart / Checkout block, whose own button is the CTA. Applies to both A/B variants.
isConditionalbooleanNoundefinedWhen true, enables conditional routing based on a question's selected option.
conditionalQuestionIndexnumberNoundefined0-based index into subEls pointing to the question used for routing.
isABTestbooleanNoundefinedWhen true, enables A/B test mode. See A/B Testing section.
variantAobjectNoundefinedVariant A content (replaces subEls for variant A visitors).
variantBobjectNoundefinedVariant B content.
variantAWeightnumberNo50Traffic percentage for Variant A (0-100). Variant B gets the remainder.

Conditional Routing: When isConditional: true, the node renders a separate output handle for each option in the referenced question. Edges connect from handles like option-0, option-1, etc.


Sub-Elements

Sub-elements are the content blocks inside a Step Node's subEls array. Each has a kind field that determines its type.

title

Heading text displayed prominently.

{ "kind": "title", "text": "What is your biggest challenge?" }
FieldTypeRequiredDefaultDescription
kind"title"YesDiscriminator.
textstringYesThe heading text. Plain text or rich text.
isRichTextbooleanNofalseWhether text contains rich text formatting.
colorstringNoundefinedHex color override (e.g. "#FF0000").
contentFormatstringNoundefined"markdown" or "html". Format of rich text content.

text

Body text / paragraph content.

{ "kind": "text", "text": "Select the option that best describes you." }
FieldTypeRequiredDefaultDescription
kind"text"YesDiscriminator.
textstringYesThe body text content.
isRichTextbooleanNofalseWhether text contains rich text formatting.
colorstringNoundefinedHex color override.
contentFormatstringNoundefined"markdown" or "html".

Rich text example with HTML:

{
  "kind": "text",
  "text": "<p style=\"text-align: center;\"><span style=\"font-size: 28px;\">How old are you?</span></p>",
  "isRichText": true,
  "contentFormat": "html"
}

image

Displays one or more images.

{ "kind": "image", "url": "https://example.com/hero.png", "alt": "Hero image" }
FieldTypeRequiredDefaultDescription
kind"image"YesDiscriminator.
urlstringYesPrimary image URL.
altstringNoundefinedAlt text for accessibility.
imagesArray<{ url, alt? }>NoundefinedAdditional images (for carousels/galleries).

question

Interactive question with selectable options. The most important sub-element for quiz funnels.

{
  "kind": "question",
  "kindOf": "single",
  "options": [
    { "id": "opt_1", "text": "Focus issues", "emoji": "\ud83c\udfaf" },
    { "id": "opt_2", "text": "Time management", "emoji": "\u23f0" }
  ],
  "layout": "list",
  "variableName": "challenge"
}
FieldTypeRequiredDefaultDescription
kind"question"YesDiscriminator.
kindOf"single" or "multi"Yes"single" = one selection. "multi" = multiple selections.
optionsarrayYesArray of options. Can be plain strings or rich option objects.
layout"list" or "grid"No"list"Visual layout of the options.
variableNamestringNoundefinedVariable name for storing the selected answer.

Plain string options (simplest form):

{ "kind": "question", "kindOf": "single", "options": ["Yes", "No", "Maybe"] }

Rich option objects (recommended for most funnels):

{
  "id": "opt_abc123",
  "text": "Focus issues",
  "emoji": "\ud83c\udfaf",
  "imageUrl": "https://example.com/focus.png",
  "subtext": "Difficulty concentrating on tasks",
  "subtextEmoji": "\ud83d\udcad",
  "subtextColor": "#6B7280"
}
FieldTypeRequiredDefaultDescription
idstringNo*Auto-generatedUnique option ID. Required for conditional routing & If/Else conditions.
textstringYesPrimary display text. Keep short (1-4 words ideal, 6 max).
textHtmlstringNoundefinedDisplay only. Rich-text version of text (see below).
emojistringNoundefinedEmoji displayed before the text.
imageUrlstringNoundefinedImage URL displayed in the option card.
subtextstringNoundefinedSecondary text below the main text.
subtextEmojistringNoundefinedEmoji shown to the left of subtext.
subtextColorstringNoundefinedHex color override for subtext.

Option text tips: Keep text short and scannable. Use subtext for additional context instead of long option text. Use emojis selectively — they help users scan faster but skip them on age ranges, yes/no, or numbers.

Formatting part of an option (textHtml): to emphasise part of a label — Best for <strong>fast</strong> results — set textHtml alongside text. text stays the plain source of truth: it is what analytics, response variables, webhooks and CRM payloads receive, so it must match textHtml with the tags stripped. If the two disagree, the plain text renders and textHtml is ignored — so when you edit a label, update both or drop textHtml. The editor keeps them in sync for you.

Supported tags: p, br, strong, b, em, i, u, s, del, mark, span. The only attribute is style, limited to color, background-color, font-weight, font-style and text-decoration — anything else is stripped. Font size and alignment are deliberately not supported: option sizing follows the funnel's audience typography setting.

loading

Displays a loading/progress animation. Three styles available.

Bar style (simple progress bar, best for 2-5s transitions):

{ "kind": "loading", "seconds": 4, "style": "bar", "text": "Analyzing your answers..." }

Circle style (circular spinner, best for 3-6s mid-funnel processing):

{ "kind": "loading", "seconds": 5, "style": "circle", "text": "Building your plan..." }

Steps style (multi-step loader, most engaging, best before results, 15-30s):

{
  "kind": "loading",
  "seconds": 15,
  "style": "steps",
  "steps": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "loadingText": "Analyzing your profile...",
      "completedText": "Profile analyzed",
      "seconds": 5,
      "modal": null
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "loadingText": "Building your plan...",
      "completedText": "Plan ready",
      "seconds": 5,
      "modal": {
        "warningText": "To personalize your plan",
        "questionText": "Do you prefer morning or evening routines?",
        "buttonLabelYes": "Morning",
        "buttonLabelNo": "Evening"
      }
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "loadingText": "Finalizing recommendations...",
      "completedText": "Recommendations ready",
      "seconds": 5
    }
  ]
}
FieldTypeRequiredDefaultDescription
kind"loading"YesDiscriminator.
secondsnumberYesTotal duration in seconds.
stylestringNo"bar""bar", "circle", or "steps".
textstringNoundefinedText shown during loading (for bar/circle styles).
stepsarrayNoundefinedMulti-step config. Used when style is "steps".

Loader step fields:

FieldTypeRequiredDescription
idstringYesUnique step ID. Use UUID format.
loadingTextstringYesText shown while this step is loading.
completedTextstringYesText shown when this step completes (with checkmark).
secondsnumberYesDuration for this step (1-60 seconds).
modalobjectNoOptional modal that pauses the loader at 50%.

Modal fields (when present on a loader step):

FieldTypeRequiredDescription
warningTextstringYesWarning/context text.
questionTextstringYesThe question to ask.
buttonLabelYesstringYesAffirmative button label.
buttonLabelNostringYesNegative button label.

Use modals sparingly — 1-2 per multi-step loader max.

custom_html

Renders arbitrary HTML content or a pre-built interactive block.

Inline HTML:

{
  "kind": "custom_html",
  "html": "<div style='text-align:center; padding:20px;'><h2>Your Results</h2><p>Based on your answers...</p></div>"
}

Pre-built block (uses templateId + templateConfig, html must be empty string):

{
  "kind": "custom_html",
  "html": "",
  "templateId": "reviews1",
  "templateConfig": {
    "reviews": [
      { "title": "Amazing product", "body": "Really works...", "authorName": "Sarah M." }
    ],
    "starColor": "#F5A623",
    "verifiedColor": "#2ECC87",
    "autoRotateMs": 4000
  }
}
FieldTypeRequiredDefaultDescription
kind"custom_html"YesDiscriminator.
htmlstringYesRaw HTML content. Must be "" when using a pre-built block.
templateIdstringNoundefinedPre-built block identifier. See Pre-Built Blocks section.
templateConfigobjectNoundefinedConfiguration for the pre-built block.

input

Single-line text input field for collecting user data.

{
  "kind": "input",
  "label": "Your email",
  "placeholder": "you@example.com",
  "inputType": "email",
  "required": true,
  "variableName": "user_email",
  "klaviyoEnabled": true
}
FieldTypeRequiredDefaultDescription
kind"input"YesDiscriminator.
labelstringNoundefinedLabel text above the input.
placeholderstringNoundefinedPlaceholder text inside the input.
inputTypestringYesOne of: "text", "email", "number", "tel", "url", "password".
requiredbooleanNoundefinedWhether the field must be filled before proceeding.
minLengthnumberNoundefinedMinimum character length.
maxLengthnumberNoundefinedMaximum character length.
klaviyoEnabledbooleanNoundefinedWhether to sync this input to Klaviyo.
variableNamestringNoundefinedVariable name for storing the value.

dropdown

Dropdown select menu.

{
  "kind": "dropdown",
  "label": "Your country",
  "placeholder": "Select a country",
  "options": [
    { "id": "opt_us", "text": "United States", "emoji": "\ud83c\uddfa\ud83c\uddf8" },
    { "id": "opt_uk", "text": "United Kingdom", "emoji": "\ud83c\uddec\ud83c\udde7" }
  ],
  "defaultOptionIndex": 0,
  "required": true,
  "variableName": "country"
}
FieldTypeRequiredDefaultDescription
kind"dropdown"YesDiscriminator.
labelstringNoundefinedLabel text above the dropdown.
placeholderstringNoundefinedPlaceholder text when no option is selected.
optionsarrayYesArray of dropdown options ({ id?, text, emoji? }).
defaultOptionIndexnumberNoundefined0-based index of the pre-selected option.
requiredbooleanNoundefinedWhether a selection is required.
variableNamestringNoundefinedVariable name for storing the value.

Exit Node

Terminal node that redirects the end-user to an external URL.

{
  "id": "exit_1710400000000_abc123def",
  "type": "exitNode",
  "position": { "x": 900, "y": 200 },
  "data": {
    "name": "Exit",
    "redirectUrl": "https://example.com/thank-you",
    "size": { "w": 280, "h": 200 }
  },
  "width": 280,
  "height": 200
}
FieldTypeRequiredDefaultDescription
namestringNo"Exit"Display name in canvas.
redirectUrlstringYes""URL the end-user is redirected to. Can be empty string.
size{ w, h }Yes{ w: 280, h: 200 }Internal size dimensions.

If/Else Node

Conditional branching node that routes users based on their previous answers.

{
  "id": "ifelse_1710400000000_abc123def",
  "type": "ifElseNode",
  "position": { "x": 500, "y": 200 },
  "data": {
    "name": "Age Router",
    "conditions": [
      {
        "id": "cond_young",
        "label": "If",
        "criteria": [
          { "stepId": "step_q1", "questionIndex": 0, "optionIds": ["opt_under18"] }
        ],
        "handleId": "condition-if",
        "type": "if"
      },
      {
        "id": "cond_adult",
        "label": "Else If 1",
        "criteria": [
          { "stepId": "step_q1", "questionIndex": 0, "optionIds": ["opt_18to35"] }
        ],
        "handleId": "condition-elseif-1",
        "type": "elseIf"
      },
      {
        "id": "cond_fallback",
        "label": "Else",
        "criteria": [],
        "handleId": "condition-else",
        "type": "else"
      }
    ],
    "size": { "w": 240, "h": 180 }
  },
  "width": 240,
  "height": 180
}
FieldTypeRequiredDefaultDescription
namestringNo"If/Else"Display name in canvas.
conditionsarrayYesArray of conditions.
size{ w, h }Yes{ w: 240, h: 180 }Internal size.

Condition fields:

FieldTypeRequiredDescription
idstringYesUnique condition ID. Used as sourceHandle in edges.
labelstringYesDisplay label: "If", "Else If 1", "Else If 2", ..., "Else".
criteriaarrayYesArray of criteria (AND logic). Empty [] for Else.
handleIdstringYesInternal handle: "condition-if", "condition-elseif-1", "condition-else".
typestringYes"if", "elseIf", or "else".

Criteria fields:

FieldTypeRequiredDescription
stepIdstringYesID of the Step Node containing the question to evaluate.
questionIndexnumberYes0-based index of the question in that step's subEls array.
optionIdsstring[]YesOption IDs that must be selected. AND logic: all must match.

Condition ordering rules:

  1. First condition must be type: "if"
  2. Last condition must be type: "else"
  3. Any number of type: "elseIf" conditions between them
  4. Between criteria in one condition: AND logic (all must be satisfied)
  5. Between conditions: evaluated top to bottom, first match wins

Edges

Edges connect nodes together. Each edge in the edges array:

{
  "id": "edge_1710400000000_abc123def",
  "source": "step_1710400000000_node1",
  "target": "step_1710400000001_node2",
  "sourceHandle": "output",
  "targetHandle": "input",
  "type": "deletable",
  "animated": false,
  "style": { "stroke": "#374151", "strokeWidth": 2 }
}
FieldTypeRequiredDefaultDescription
idstringYesUnique edge ID.
sourcestringYesID of the source (outgoing) node.
targetstringYesID of the target (incoming) node.
sourceHandlestringYes"output"Output handle on the source node. See table below.
targetHandlestringYes"input"Always "input".
typestringYes"deletable"Always "deletable" for Clarflow edges.
animatedbooleanNofalseWhether the edge has a flow animation.
styleobjectNoStandard: { "stroke": "#374151", "strokeWidth": 2 }.

Source handle types:

Handle PatternUsed ForExample
"output"Default (non-conditional) edge"output"
"option-{index}"Conditional routing on step"option-0", "option-1"
"variantA-option-{index}"A/B test variant A conditional"variantA-option-0"
"variantB-option-{index}"A/B test variant B conditional"variantB-option-1"
Condition idIf/Else node edgesThe condition's id value (e.g. "cond_young")

Important for If/Else edges: The sourceHandle must be the condition's id field, NOT the handleId pattern. The condition id is what edges reference.


A/B Testing

When isABTest: true on a Step Node, the node shows two variants to different visitors.

Variant structure:

{
  "subEls": [ ... ],
  "buttonText": "Continue",
  "isConditional": false,
  "conditionalQuestionIndex": 0
}
FieldTypeRequiredDefaultDescription
subElsarrayYesSub-elements for this variant.
buttonTextstringNoundefinedCustom button text for this variant.
isConditionalbooleanNoundefinedEnable per-variant conditional routing.
conditionalQuestionIndexnumberNoundefinedIndex of the question in this variant's subEls for routing.

A/B test node example:

{
  "id": "step_abtest_1",
  "type": "quizStep",
  "position": { "x": 500, "y": 200 },
  "data": {
    "name": "A/B Test Step",
    "subEls": [],
    "size": { "w": 280, "h": 200 },
    "isABTest": true,
    "variantAWeight": 50,
    "variantA": {
      "subEls": [
        { "kind": "title", "text": "Version A: Do you struggle with focus?" },
        { "kind": "question", "kindOf": "single", "options": ["Yes", "No"] }
      ],
      "buttonText": "Next"
    },
    "variantB": {
      "subEls": [
        { "kind": "title", "text": "Version B: How's your concentration?" },
        { "kind": "question", "kindOf": "single", "options": ["Good", "Could be better"] }
      ],
      "buttonText": "Continue"
    }
  },
  "width": 280,
  "height": 400
}

Height rule: For A/B test nodes, set height to size.h * 2 (e.g. 200 * 2 = 400).


Pre-Built Blocks

Pre-built blocks are config-driven interactive components. When using a pre-built block, set html to an empty string "" and provide templateId + templateConfig.

{
  "kind": "custom_html",
  "html": "",
  "templateId": "reviews1",
  "templateConfig": { ... }
}

Available Blocks

CategoryBlock IDDescription
Reviewreviews1Review carousel with 5-star ratings and verified badges
Reviewreviews2-trustpilotTrustpilot-style review carousel
Reviewreviews3Auto-scrolling horizontal review slider (dark/premium aesthetic)
OfferofferFull offer card with badge, hero image, features, trust badges
Offeroffer2Simpler product card with pricing and bullet benefits
Interactivebefore-afterDrag slider to compare two images side by side
Interactivescratch-to-revealScratch card with confetti animation on reveal
Interactiveswipe-statementsTinder-style swipe cards for yes/no statements
Graphgraph-goalGoal progression chart with animated trend line
Assessmentnoise-profileSeverity assessment card with slider and stat cards
Graphresults-by-dateMonth-over-month bar chart with projected results

reviews1 — Review Carousel

Auto-rotating review carousel with 5-star ratings and verified badges.

{
  "templateId": "reviews1",
  "templateConfig": {
    "reviews": [
      { "title": "This actually works", "body": "I was skeptical but after 3 weeks...", "authorName": "Sarah M." },
      { "title": "Best purchase this year", "body": "Noticed results within days...", "authorName": "John G." },
      { "title": "Highly recommend", "body": "My friend recommended this and...", "authorName": "Lisa K." }
    ],
    "starColor": "#F5A623",
    "verifiedColor": "#2ECC87",
    "autoRotateMs": 4000
  }
}
FieldTypeRequiredDefaultDescription
reviewsarrayYesArray of { title, body, authorName }. 3-5 recommended.
starColorstringNo"#F5A623"Hex color for star icons.
verifiedColorstringNo"#2ECC87"Hex color for "Verified" badge.
autoRotateMsnumberNo4000Auto-rotation interval in milliseconds.

reviews2-trustpilot — Trustpilot Reviews

Trustpilot-style review carousel with green square stars.

{
  "templateId": "reviews2-trustpilot",
  "templateConfig": {
    "reviews": [
      { "title": "Eye-opening information", "body": "Detailed review text...", "authorName": "Patrick N." },
      { "title": "Changed my life", "body": "Another review...", "authorName": "Brian R." }
    ],
    "autoRotateMs": 4000
  }
}
FieldTypeRequiredDefaultDescription
reviewsarrayYesSame structure as reviews1.
autoRotateMsnumberNo4000Auto-rotation interval.

reviews3 — Review Slider

Auto-scrolling horizontal review carousel with overall rating badge. Dark/premium aesthetic.

{
  "templateId": "reviews3",
  "templateConfig": {
    "headerLabel": "Loved by thousands",
    "overallRating": "4.8",
    "primaryColor": "#CA5839",
    "bgGradientStart": "#2e1c18",
    "bgGradientEnd": "#3d2520",
    "textColor": "#ffffff",
    "reviewTextColor": "rgba(255,255,255,0.85)",
    "reviewCardBg": "rgba(202,88,57,0.1)",
    "reviews": [
      { "text": "Review text without a separate title...", "authorName": "Sarah M." },
      { "text": "Another review...", "authorName": "Mike T." }
    ]
  }
}
FieldTypeRequiredDefaultDescription
headerLabelstringNo"Loved by thousands"Header text above reviews.
overallRatingstringNo"4.8"Overall rating in badge.
primaryColorstringNo"#CA5839"Accent color.
bgGradientStartstringNo"#2e1c18"Background gradient start.
bgGradientEndstringNo"#3d2520"Background gradient end.
textColorstringNo"#ffffff"Main text color.
reviewTextColorstringNo"rgba(255,255,255,0.85)"Review body text color.
reviewCardBgstringNo"rgba(202,88,57,0.1)"Review card background.
reviewsarrayYesReviews with { text, authorName } (note: text not body, no title).

offer — Offer Card

Full-featured offer card with badge, hero image, feature tags, offer box, and trust badges.

{
  "templateId": "offer",
  "templateConfig": {
    "badgeText": "Your Personalized Plan",
    "headlineHtml": "<p style=\"text-align: center\"><span style=\"font-size: 22px\">Transform your health — <span style=\"color: #D4488C\">or pay nothing.</span></span></p>",
    "descriptionHtml": "<p style=\"text-align: center; font-size: 12px; color: #5C5C5C\">Join 25,000+ people who saw results...</p>",
    "imageUrl": "https://example.com/product.jpeg",
    "feature1": "Results in 28 days",
    "feature2": "Science-backed formula",
    "feature3": "Easy daily routine",
    "socialProofText": "4.9/5 from 14,207 ratings",
    "starColor": "#E88BA0",
    "accentColor": "#D4488C",
    "offerTextHtml": "<p style=\"font-size: 12px; color: #5C5C5C\"><span style=\"color: #D4488C; font-weight: 700\">QUIZ RESULTS OFFER:</span> Unlock <span style=\"font-weight: 700; color: #1A1A1A\">up to 60% off</span>...</p>",
    "trustBadge1": "90-day guarantee",
    "trustBadge2": "Clinically tested",
    "trustBadge3": "Free shipping"
  }
}
FieldTypeRequiredDescription
badgeTextstringYesTop badge/eyebrow text.
headlineHtmlstringYesRich HTML headline.
descriptionHtmlstringYesRich HTML description below headline.
imageUrlstringYesHero product image URL.
feature1stringYesFeature tag 1.
feature2stringYesFeature tag 2.
feature3stringYesFeature tag 3.
socialProofTextstringNoRating line beside the stars. Defaults to "4.9/5 from 14,207 ratings"; set "" to hide the star row. The stars fill to the score stated here — "4.5/5", "4.5 out of 5" and "4.5 stars" all render four and a half (. or , decimals). A line with no score leaves the stars full.
starRatingnumberNoFills the stars to this value instead. Only needed when socialProofText states no score.
starColorstringNoStar fill color (hex). Defaults to "#E88BA0". Unfilled stars are the same color at 25% opacity.
accentColorstringYesAccent/brand color (hex).
offerTextHtmlstringYesRich HTML offer details.
trustBadge1stringYesTrust badge 1 text.
trustBadge2stringYesTrust badge 2 text.
trustBadge3stringYesTrust badge 3 text.

offer2 — Product Card

Simpler product card with image, pricing, features, and benefit bullets.

{
  "templateId": "offer2",
  "templateConfig": {
    "imageUrl": "https://example.com/product.webp",
    "badgeText": "RECOMMENDED FOR YOU",
    "productName": "Product Name",
    "featuresText": "Benefit 1 \u2022 Benefit 2 \u2022 Benefit 3",
    "originalPrice": "$89",
    "discountedPrice": "$39",
    "priceCopyHtml": "<p style=\"font-size: 13px; color: rgba(13,27,42,0.72)\">Your quiz unlocked a <strong>$50 credit</strong>...</p>",
    "accentColor": "#5BA4CF",
    "whyTitleHtml": "<p style=\"font-size: 16px; font-weight: 900; text-align: center\">Why this fits your profile</p>",
    "bullets": [
      "First benefit explanation",
      "Second benefit explanation",
      "Third benefit explanation",
      "Fourth benefit explanation"
    ]
  }
}
FieldTypeRequiredDescription
imageUrlstringYesProduct image URL.
badgeTextstringYesTop badge text.
productNamestringYesProduct name.
featuresTextstringYesInline features text (dot-separated).
originalPricestringYesOriginal/strikethrough price.
discountedPricestringYesDiscounted/current price.
priceCopyHtmlstringYesRich HTML price explanation.
accentColorstringYesAccent color (hex).
whyTitleHtmlstringYesRich HTML "why this fits" title.
bulletsstring[]YesArray of benefit bullet strings (4-6 recommended).

before-after — Image Comparison Slider

Interactive drag slider to compare two images side by side.

{
  "templateId": "before-after",
  "templateConfig": {
    "beforeImageUrl": "https://example.com/before.jpeg",
    "afterImageUrl": "https://example.com/after.jpeg",
    "beforeLabel": "Before",
    "afterLabel": "After"
  }
}
FieldTypeRequiredDefaultDescription
beforeImageUrlstringYes"Before" image URL.
afterImageUrlstringYes"After" image URL.
beforeLabelstringNo"Before"Label for the before side.
afterLabelstringNo"After"Label for the after side.

scratch-to-reveal — Scratch Card

Interactive scratch card with canvas overlay. User scratches to reveal a reward with confetti animation.

{
  "templateId": "scratch-to-reveal",
  "templateConfig": {
    "titleHtml": "<strong>You've Unlocked a <span style=\"color: #CA5839\">Mystery Reward</span></strong>",
    "subtitleText": "Scratch below to reveal your exclusive offer",
    "primaryColor": "#CA5839",
    "scratchBrushSize": 22,
    "revealThreshold": 50,
    "canvasHeight": 340,
    "rewardEyebrow": "Your Exclusive Reward",
    "rewardValue": "FREE",
    "rewardItem": "Growth Activation Serum",
    "rewardDescription": "$49 value - Added to your order",
    "goldColor": "#b8860b",
    "instructionText": "Scratch to Reveal",
    "instructionSubText": "Use your finger to scratch",
    "enableConfetti": true,
    "confettiColors": [],
    "maxWidth": 340,
    "borderRadius": 12
  }
}
FieldTypeRequiredDefaultDescription
titleHtmlstringYesRich HTML title above scratch area.
subtitleTextstringNoSubtitle below title.
primaryColorstringNo"#CA5839"Primary accent color.
scratchBrushSizenumberNo22Scratch brush radius in pixels.
revealThresholdnumberNo50% of area to scratch before auto-reveal.
canvasHeightnumberNo340Scratch canvas height in pixels.
rewardEyebrowstringYesSmall text above reward value.
rewardValuestringYesBig reward value text (e.g. "FREE", "50% OFF").
rewardItemstringYesItem/product name.
rewardDescriptionstringYesDescription below reward.
goldColorstringNo"#b8860b"Gold accent for reward display.
instructionTextstringNo"Scratch to Reveal"Instruction overlay text.
instructionSubTextstringNoSecondary instruction text.
enableConfettibooleanNotrueEnable confetti on reveal.
confettiColorsstring[]No[]Custom confetti colors (empty = defaults).
maxWidthnumberNo340Max width in pixels.
borderRadiusnumberNo12Border radius in pixels.

swipe-statements — Tinder-Style Cards

Tinder-style swipe cards where users swipe right for yes, left for no.

{
  "templateId": "swipe-statements",
  "templateConfig": {
    "titleText": "Does this sound like you?",
    "subtitleText": "Swipe right for yes, left for no",
    "primaryColor": "#CA5839",
    "yesColor": "#22c55e",
    "noColor": "#ef4444",
    "cardBgColor": "#ffffff",
    "textColor": "#1a1a1a",
    "subtextColor": "#555555",
    "yesLabel": "Yes",
    "noLabel": "Nope",
    "hintText": "Swipe or use buttons below",
    "completeTitle": "We've got your profile!",
    "completeSubtitle": "You matched with {count} common concerns",
    "cards": [
      { "emoji": "\ud83e\ude9e", "text": "I've noticed changes recently" },
      { "emoji": "\ud83d\ude29", "text": "I've tried other products with little results" },
      { "emoji": "\ud83c\udf3f", "text": "I prefer natural, science-backed solutions" },
      { "emoji": "\u23f0", "text": "I want to act before it gets worse" },
      { "emoji": "\ud83d\udcaa", "text": "I'm ready to commit to a daily routine" }
    ],
    "maxWidth": 360,
    "cardHeight": 320
  }
}
FieldTypeRequiredDefaultDescription
titleTextstringYesTitle above the card stack.
subtitleTextstringNoSubtitle/instruction text.
primaryColorstringNo"#CA5839"Primary accent color.
yesColorstringNo"#22c55e"Color for "yes" swipe/button.
noColorstringNo"#ef4444"Color for "no" swipe/button.
cardBgColorstringNo"#ffffff"Card background color.
textColorstringNo"#1a1a1a"Main text color.
subtextColorstringNo"#555555"Secondary text color.
yesLabelstringNo"Yes"Yes button label.
noLabelstringNo"Nope"No button label.
hintTextstringNoHint text below cards.
completeTitlestringNoTitle shown after all cards swiped.
completeSubtitlestringNoSubtitle after completion. {count} is replaced with yes-count.
cardsarrayYesArray of { emoji, text } cards (4-8 recommended).
maxWidthnumberNo360Max card width in pixels.
cardHeightnumberNo320Card height in pixels.

graph-goal — Goal Progression Chart

Animated goal progression chart showing growth/decline over time.

{
  "templateId": "graph-goal",
  "templateConfig": {
    "yAxisLabels": [
      "HIGH\nLevels",
      "NORMAL\nLevels",
      "LOW\nLevels"
    ],
    "xAxisLabels": ["TODAY", "MONTH 1", "MONTH 2", "MONTH 3", "MONTH 4"],
    "youAreHereLabel": "YOU ARE HERE",
    "goalLabel": "GOAL: Significant Improvement",
    "trend": "upward"
  }
}
FieldTypeRequiredDefaultDescription
yAxisLabelsstring[]YesY-axis labels (3 items: high, normal, low). Use \n for line breaks.
xAxisLabelsstring[]YesX-axis labels (time periods, 4-6 items).
youAreHereLabelstringNo"YOU ARE HERE"Label for the starting point.
goalLabelstringNoLabel for the goal/target point.
trendstringYes"upward" or "downward" — direction of the trend line.

noise-profile — Severity Assessment Card

Severity assessment display with slider, alert box, and stat cards.

{
  "templateId": "noise-profile",
  "templateConfig": {
    "cardTitle": "Your Assessment Results",
    "levelPillText": "Severe",
    "levelPillColor": "#C0392B",
    "imageUrl": "https://example.com/assessment.webp",
    "sliderTooltipText": "Your level",
    "sliderLabels": ["Balanced", "Mild", "Moderate", "Severe"],
    "sliderEndPosition": 90,
    "alertTitle": "SEVERE imbalance detected",
    "alertText": "Based on your responses, your levels are critically disrupted...",
    "stats": [
      { "emoji": "\ud83e\udda0", "bgColor": "#FEF3E2", "label": "Primary metric", "value": "Critically Low", "isHighlighted": true },
      { "emoji": "\ud83d\udca8", "bgColor": "#E3F2FD", "label": "Secondary metric", "value": "Severe", "isHighlighted": true },
      { "emoji": "\ud83e\udde0", "bgColor": "#FCE4EC", "label": "Tertiary metric", "value": "Elevated", "isHighlighted": true },
      { "emoji": "\u26a1", "bgColor": "#E8EAF6", "label": "Fourth metric", "value": "Below 40%", "isHighlighted": true }
    ]
  }
}
FieldTypeRequiredDefaultDescription
cardTitlestringYesMain card title.
levelPillTextstringYesSeverity level pill text.
levelPillColorstringYesSeverity pill color (hex).
imageUrlstringNoOptional image URL.
sliderTooltipTextstringNo"Your level"Tooltip on the severity slider.
sliderLabelsstring[]YesSlider scale labels (4 items: low to high).
sliderEndPositionnumberYesSlider position 0-100.
alertTitlestringYesAlert box title.
alertTextstringYesAlert box description.
statsarrayYesArray of stat cards (3-4 recommended).
stats[].emojistringYesStat card emoji.
stats[].bgColorstringYesStat card background color (hex).
stats[].labelstringYesStat metric label.
stats[].valuestringYesStat value text.
stats[].isHighlightedbooleanNofalseWhether to visually highlight this stat.

results-by-date — Month-Over-Month Bar Chart

Animated bar chart showing projected results over time.

{
  "templateId": "results-by-date",
  "templateConfig": {
    "titleHtml": "<p style=\"text-align: center; font-size: 28px; font-weight: 700\">The last plan you'll ever need</p>",
    "descriptionHtml": "<p style=\"text-align: center; font-size: 16px; color: #555\">Based on your answers, we expect visible results by May 2026</p>",
    "trend": "upward",
    "startMonth": "February",
    "endMonth": "May"
  }
}
FieldTypeRequiredDescription
titleHtmlstringYesRich HTML title (use inline styles).
descriptionHtmlstringYesRich HTML description.
trendstringYes"upward" or "downward".
startMonthstringYesStarting month name (e.g. "February").
endMonthstringYesEnding month name (e.g. "May").

ID Conventions

IDs use a timestamp + random suffix pattern for uniqueness:

EntityPatternExample
Step Nodestep_{timestamp}_{random}step_1710400000000_k7x2m9abc
Exit Nodeexit_{timestamp}_{random}exit_1710400000001_p3y8n5def
If/Else Nodeifelse_{timestamp}_{random}ifelse_1710400000002_q1w2e3ghi
Start Nodestart_{timestamp}_{random}start_1710400000003_r4t5y6jkl
Edgeedge_{timestamp}_{random}edge_1710400000004_u7i8o9mno
Conditioncondition_{timestamp}_{random}condition_1710400000005_a1s2d3pqr
Optionopt_{timestamp}_{random}opt_1710400000006_f4g5h6stu
Loader StepUUID formata1b2c3d4-e5f6-7890-abcd-ef1234567890
  • {timestamp} = Date.now() (milliseconds since epoch)
  • {random} = 9-character random alphanumeric string

When pasting, the system automatically remaps all IDs to new unique values. So the exact IDs don't need to be globally unique — they just need to be internally consistent (e.g., an edge's source must match a node's id).


Positioning

Linear flow: Space nodes 400px apart horizontally, constant Y.

position.x = startX + (stepIndex * 400)
position.y = 200

Branching: Offset branches vertically.

Main path:   y = 200
Branch A:    y = 50
Branch B:    y = 350

Variable References

When a quiz captures user input (via variableName on questions, inputs, or dropdowns), you can reference that value in later title/text elements using double-bracket syntax:

{{variable_name}}

For example, if an input stores to variableName: "first_name", a later title can use:

{ "kind": "title", "text": "Great news, {{first_name}}!" }

Validation Rules & Common Pitfalls

The paste deserializer checks:

  1. The parsed object must have type === "clarflow-canvas-nodes" (exact match)
  2. nodes must be an array
  3. edges must be an array
  4. If any check fails, the paste is silently ignored (no error shown)
PitfallSymptomFix
Missing type: "clarflow-canvas-nodes"Paste does nothingAdd the type field
nodes or edges not an arrayPaste does nothingEnsure both are arrays, even if empty
Option IDs missing on conditional questionsEdges connect to wrong optionsAdd explicit id fields to all options
A/B test node height not doubledNode renders incorrectlySet height to size.h * 2
If/Else edge uses handleId instead of condition idEdge doesn't connectUse the condition's id as sourceHandle
Invalid kind on sub-elementElement doesn't renderMust be: title, text, image, question, loading, custom_html, input, dropdown
kindOf missing on questionQuestion may not renderMust be "single" or "multi"
inputType missing on inputInput may not renderMust be: text, email, number, tel, url, password
Pre-built block with non-empty htmlBlock may not render correctlySet html to "" when using templateId

Complete Examples

Example 1: Minimal Single Step

A single question step with no connections:

{
  "type": "clarflow-canvas-nodes",
  "nodes": [
    {
      "id": "step_1_a",
      "type": "quizStep",
      "position": { "x": 300, "y": 200 },
      "data": {
        "name": "Quick Question",
        "subEls": [
          { "kind": "title", "text": "How did you hear about us?" },
          {
            "kind": "question",
            "kindOf": "single",
            "options": ["Google", "Friend", "Social Media", "Other"]
          }
        ],
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    }
  ],
  "edges": [],
  "timestamp": 1710400000000
}

Example 2: Two Connected Steps

Two steps connected by a default edge:

{
  "type": "clarflow-canvas-nodes",
  "nodes": [
    {
      "id": "step_1_intro",
      "type": "quizStep",
      "position": { "x": 100, "y": 200 },
      "data": {
        "name": "Introduction",
        "subEls": [
          { "kind": "title", "text": "Welcome to our assessment" },
          { "kind": "text", "text": "This will take about 2 minutes." }
        ],
        "size": { "w": 280, "h": 200 },
        "buttonText": "Let's Go"
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "step_2_question",
      "type": "quizStep",
      "position": { "x": 500, "y": 200 },
      "data": {
        "name": "Main Question",
        "subEls": [
          { "kind": "title", "text": "What is your primary goal?" },
          {
            "kind": "question",
            "kindOf": "single",
            "options": [
              { "id": "opt_focus", "text": "Better focus", "emoji": "\ud83c\udfaf" },
              { "id": "opt_sleep", "text": "Better sleep", "emoji": "\ud83d\ude34" },
              { "id": "opt_stress", "text": "Less stress", "emoji": "\ud83e\uddd8" }
            ],
            "layout": "list"
          }
        ],
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    }
  ],
  "edges": [
    {
      "id": "edge_1_2",
      "source": "step_1_intro",
      "target": "step_2_question",
      "sourceHandle": "output",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    }
  ],
  "timestamp": 1710400000000
}

Example 3: Conditional Routing

A step with conditional routing that sends users to different nodes based on their answer:

{
  "type": "clarflow-canvas-nodes",
  "nodes": [
    {
      "id": "step_q1",
      "type": "quizStep",
      "position": { "x": 100, "y": 200 },
      "data": {
        "name": "Router Question",
        "subEls": [
          { "kind": "title", "text": "Are you a morning or evening person?" },
          {
            "kind": "question",
            "kindOf": "single",
            "options": [
              { "id": "opt_morning", "text": "Morning person", "emoji": "\ud83c\udf05" },
              { "id": "opt_evening", "text": "Evening person", "emoji": "\ud83c\udf19" }
            ]
          }
        ],
        "size": { "w": 280, "h": 200 },
        "isConditional": true,
        "conditionalQuestionIndex": 0
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "step_morning",
      "type": "quizStep",
      "position": { "x": 500, "y": 100 },
      "data": {
        "name": "Morning Path",
        "subEls": [
          { "kind": "title", "text": "Great! Early birds get the worm." }
        ],
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "step_evening",
      "type": "quizStep",
      "position": { "x": 500, "y": 350 },
      "data": {
        "name": "Evening Path",
        "subEls": [
          { "kind": "title", "text": "Night owls are creative!" }
        ],
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    }
  ],
  "edges": [
    {
      "id": "edge_morning",
      "source": "step_q1",
      "target": "step_morning",
      "sourceHandle": "option-0",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    },
    {
      "id": "edge_evening",
      "source": "step_q1",
      "target": "step_evening",
      "sourceHandle": "option-1",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    }
  ],
  "timestamp": 1710400000000
}

option-0 maps to the first option ("Morning person"), option-1 to the second ("Evening person"). The index corresponds to the option's position in the options array.

Example 4: Full Flow with Email Capture, Loading, and Exit

A complete flow showcasing multiple sub-element types:

{
  "type": "clarflow-canvas-nodes",
  "nodes": [
    {
      "id": "step_welcome",
      "type": "quizStep",
      "position": { "x": 100, "y": 200 },
      "data": {
        "name": "Welcome",
        "subEls": [
          { "kind": "image", "url": "https://example.com/logo.png", "alt": "Brand logo" },
          { "kind": "title", "text": "Find your perfect routine" },
          { "kind": "text", "text": "Answer a few quick questions and we'll build a personalized plan." }
        ],
        "size": { "w": 280, "h": 200 },
        "buttonText": "Start Quiz"
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "step_q1",
      "type": "quizStep",
      "position": { "x": 500, "y": 200 },
      "data": {
        "name": "Goal Selection",
        "subEls": [
          { "kind": "title", "text": "What's your #1 goal?" },
          {
            "kind": "question",
            "kindOf": "single",
            "options": [
              { "id": "opt_focus", "text": "Improve focus", "emoji": "\ud83c\udfaf", "subtext": "Stay on task longer" },
              { "id": "opt_energy", "text": "More energy", "emoji": "\u26a1", "subtext": "Feel less drained" },
              { "id": "opt_calm", "text": "Reduce anxiety", "emoji": "\ud83e\uddd8", "subtext": "Feel more at peace" }
            ],
            "layout": "list",
            "variableName": "primary_goal"
          }
        ],
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "step_email",
      "type": "quizStep",
      "position": { "x": 900, "y": 200 },
      "data": {
        "name": "Email Capture",
        "subEls": [
          { "kind": "title", "text": "Almost done!" },
          { "kind": "text", "text": "Enter your email to get your personalized results." },
          {
            "kind": "input",
            "label": "Email",
            "placeholder": "you@example.com",
            "inputType": "email",
            "required": true,
            "klaviyoEnabled": true,
            "variableName": "email"
          }
        ],
        "size": { "w": 280, "h": 200 },
        "buttonText": "Get My Results"
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "step_loading",
      "type": "quizStep",
      "position": { "x": 1300, "y": 200 },
      "data": {
        "name": "Processing",
        "subEls": [
          { "kind": "title", "text": "Building your plan..." },
          {
            "kind": "loading",
            "seconds": 8,
            "style": "steps",
            "steps": [
              {
                "id": "ls_1",
                "loadingText": "Analyzing your responses...",
                "completedText": "Responses analyzed",
                "seconds": 3
              },
              {
                "id": "ls_2",
                "loadingText": "Creating personalized plan...",
                "completedText": "Plan created",
                "seconds": 5
              }
            ]
          }
        ],
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    },
    {
      "id": "exit_results",
      "type": "exitNode",
      "position": { "x": 1700, "y": 200 },
      "data": {
        "name": "Results Page",
        "redirectUrl": "https://example.com/results",
        "size": { "w": 280, "h": 200 }
      },
      "width": 280,
      "height": 200
    }
  ],
  "edges": [
    {
      "id": "e1",
      "source": "step_welcome",
      "target": "step_q1",
      "sourceHandle": "output",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    },
    {
      "id": "e2",
      "source": "step_q1",
      "target": "step_email",
      "sourceHandle": "output",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    },
    {
      "id": "e3",
      "source": "step_email",
      "target": "step_loading",
      "sourceHandle": "output",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    },
    {
      "id": "e4",
      "source": "step_loading",
      "target": "exit_results",
      "sourceHandle": "output",
      "targetHandle": "input",
      "type": "deletable",
      "animated": false,
      "style": { "stroke": "#374151", "strokeWidth": 2 }
    }
  ],
  "timestamp": 1710400000000
}

Stop Losing 98% of Your Traffic

Join hundreds of DTC brands using Clarflow to turn browsers into buyers with AI-powered product quizzes.

Contact Sales
Free forever plan
30-day money-back guarantee
Cancel anytime