Logic settings

Just like in the Riddle creator, you can set up custom logic flows for your Riddles using the Builder API.

Custom logic flows are available for the following Riddle types via API:

Personality tests, predictors, minigames and leaderboards do not support custom logic - sending a logic property for one of them is rejected with Logic configuration is not allowed for Riddle type "<type>".

You can branch by answers (typically on SingleChoice, MultipleChoice, and standalone Form Dropdown fields), by a condition on a block's answer or a form field's value, or by block/total score. The following will be added soon:

  • Branching by a data layer variable

Any other suggestions? Please get in touch via support chat or send an email to hello@riddle.com.

Limits and constraints

  • Block count: custom logic is only accepted for Riddles with up to 250 blocks (building a logic tree is recursive, so the cost grows super-linearly). Riddles above that limit have to use the default linear logic. The overall build limit of 500 blocks still applies on top of that.
  • Pagination: custom logic and pagination are mutually exclusive. Sending logic together with preset.pagination.isEnabled: true is rejected - a paginated Riddle shows all blocks on one page, which has no meaningful branching.
  • Flashcards: a Quiz that contains a Flashcard block only accepts linear logic (see RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_LINEAR_ONLY_ALLOWED below).
  • Story: a Story Riddle only accepts linear logic - there is nothing to branch on (no answers, scores, or form fields), so answer, condition, and score branching are all rejected.
  • Every block has to be used: every block in the blocks array must be reachable somewhere in the tree, otherwise the build is rejected (see RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_UNUSED_BLOCKS).

Default logic

When you create a Riddle via the Builder API without specifying any logic settings, the Riddle will have a default linear logic flow. This means that the blocks will be shown in the exact order they are defined in the blocks array of the build configuration.

Prerequisites

To be able to work with the logic settings efficiently, it is advised to read about assigning custom IDs to blocks and answer items first. This makes it easier to reference the blocks and answer items in the following logic settings.

Logic settings structure / properties

The logic settings are defined in the logic property in the build configuration, next to the blocks and result/results properties:

{
    "type": "Quiz",
    "build": {
        "title": "My quiz with logic",
        "blocks": [
            "...",
            "...",
            "..."
        ],
        "results": [
            "..."
        ],
        "logic": {
            "...logic tree structure..."
        }
    }
}

The logic settings structure can be compared to a "tree" where the root of the tree is the start block, and each branch represents a possible path through the Riddle based on user interactions. This means it can nest indefinitely, as long as there are available blocks in the build configuration.

You can use four types of branching:

Linear 'branching'

PropertyRequiredTypeDescriptionDefault
blockIdintegerThe ID of the block where this logic layer starts. Can reference any block, for example even Content
branchingTypestringThe type of branching to use. For linear branching it is set to linear, but can also be omitted due to being the default valuelinear
nextobjectAn object defining the next block to show after the block defined in blockId has been completed. Send null (or leave it out) to mark this layer as the end of its pathnull

blockId must be a positive integer that exists in the blocks array; branchingType is matched case-insensitively (Answer works just as well as answer).

Example: Sequence of 4 linear blocks

{
    "blockId": 1,
    "next": {
        "blockId": 2,
        "next": {
            "blockId": 3,
            "next": {
                "blockId": 4
            }
        }
    }
}

In spoken form: Show block ID 1, then block ID 2, then block ID 3, then block ID 4 - and end the Riddle there.

Answer branching

PropertyRequiredTypeDescriptionDefault
blockIdintegerThe ID of the block where this logic layer starts. Can only reference blocks which offer selectable answers to branch on; see below
branchingTypestringThe type of branching to use. For answer branching, it is set to answerlinear
rulesobjectAn array of at least one rule object defining the branching logic. To see the structure of each rule, see below

Which blocks can be branched on

The block referenced by blockId has to expose selectable answer items - the API validates your answers against exactly those items:

  • Question blocks with answer items work, e.g. SingleChoice and MultipleChoice.
  • Form fields: only a standalone Dropdown field can be branched on. Any other form field - including a whole FormBuilder block - is rejected with Answer logic branching is currently only allowed with standalone Dropdown form fields (Found <type>).
  • Blocks that carry no answer items at all (e.g. a Content block) are rejected with Answer logic block must refer to a question block with choices/answers, found "<type>" block. Such blocks can still be used in linear layers.

Defining rules

Note: Each rule itself is also either a linear or answer branching, meaning it can contain a next OR rules property to define what happens after the block defined in blockId has been completed.

Properties exclusive to the rule object:

PropertyRequiredTypeDescriptionDefault
answersstring|intA non-empty array of answers that trigger this rule. You can use either the answer text or the given answer ID to identify the answer

Rules for answers:

  • Answer texts are matched exactly (no case-insensitive or trimmed matching) - if the value matches neither a title nor an ID, the error lists all valid choices of that block.
  • Every answer may only be used once per logic layer: repeating the same answer in another rule of the same layer, or twice within the same rule, is rejected as a non-unique rule.
  • Not every answer of a block needs its own rule, but every rule needs at least one answer.

Example: Answer branching with linear next

{
    "answers": ["Answer 1", "Answer 2"],
    "blockId": 2,
    "next": {
        "blockId": 3
    }
}

In spoken form: If the user selects "Answer 1" or "Answer 2" show block with ID 2, then after completing that block, show block with ID 3.

Example: Answer branching, followed by answer branching / rules

{
    "answers": ["Answer 1", "Answer 2"],
    "blockId": 2,
    "branchingType": "answer",
    "rules": [
       "..."
    ]
}

In spoken form: If the user selects "Answer 1" or "Answer 2" show block with ID 2, then after completing that block, follow the rules defined in the rules array (and so on).

Example: First linear branching, then answer branching, then linear branching

{
    "blockId": 1,
    "next": {
        "blockId": 2,
        "branchingType": "answer",
        "rules": [
            {
                "answers": ["Answer 1"],
                "blockId": 3,
                "next": {
                    "blockId": 4
                }
            },
            {
                "answers": ["Answer 2"],
                "blockId": 5
            }
        ]
    }
}

In spoken form: Start with block ID 1, then show block ID 2. If the user selects "Answer 1", show block ID 3, then block ID 4. If the user selects "Answer 2", show block ID 5.

Condition branching

PropertyRequiredTypeDescriptionDefault
blockIdintegerThe ID of the block this condition evaluates. Can reference a question block's answer, or a form field's value (including a field nested inside a FormBuilder block)
branchingTypestringThe type of branching to use. For condition branching, it is set to conditionlinear
conditionobjectAn object with field (the answer/value to compare, e.g. an answer's title or ID, or use "score" to compare a question block's own score), operator (see below), and, unless the operator is a standalone check like isAnswered, value (the value to compare against)
ifTrueobjectThe next logic layer to follow if the condition evaluates to true. Same rules as next/rules - it can itself be linear, answer, condition, or score branchingnull
ifFalseobjectThe next logic layer to follow if the condition evaluates to falsenull

Operators

The allowed operator values depend on the kind of field blockId points at:

Field kindAllowed operators
Question block answerequals, notEquals, isAnswered, isNotAnswered
Question block score (field: "score")equals, notEquals, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual
Form text fieldequals, notEquals, contains, notContains, isAnswered, isNotAnswered
Form number fieldequals, notEquals, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, isAnswered, isNotAnswered
Form select/dropdown/radio fieldequals, notEquals, isAnswered, isNotAnswered
Form checkbox fieldisChecked, isNotChecked

The value beside the operator has to fit it as well, and a mismatch is refused rather than stored: the standalone checks (isAnswered, isChecked, ...) take no value at all, the text comparisons take a single string or number, the numeric comparisons take a single number, and between / notBetween take exactly two numbers, [min, max], with min no greater than max.

A condition here addresses a block or a form field only - never the Riddle's result (magic.result.title, magic.personality.title, magic.score.percentage, ...), since the logic tree is what produces that result in the first place. Those result fields exist on exactly one surface: the conditional automation emails, see Publish settings.

Example: Condition branching on a SingleChoice answer

{
    "blockId": 1,
    "branchingType": "condition",
    "condition": {
        "field": "Berlin",
        "operator": "equals",
        "value": true
    },
    "ifTrue": {
        "blockId": 2
    },
    "ifFalse": {
        "blockId": 3
    }
}

In spoken form: Show block ID 1. If the user picked "Berlin" there, continue with block ID 2, otherwise continue with block ID 3.

Referencing a form field nested inside a FormBuilder block: use "<blockId>_<fieldId>" as the condition's blockId is still the FormBuilder block itself, while the field being evaluated is identified this way in the underlying field/value targeting - see custom IDs for how field IDs are assigned.

Score branching

PropertyRequiredTypeDescriptionDefault
blockIdintegerThe ID of the block this score is evaluated for. For scoreType: "block" this is the block whose own score is checked; for scoreType: "total" it can be any block, since the running total is riddle-wide
branchingTypestringThe type of branching to use. For score branching, it is set to scorelinear
scoreTypestringtotal branches on the cumulative score across every block answered so far (Quiz only); block branches on the score of this node's own block only
rangesobjectAn array of at least one range object mapping a [min, max] score interval to a next block. Ranges must not overlap and each range's min must be less than or equal to its max

Each range object has min (integer), max (integer), blockId (the block to show for this range) and, like an answer rule, an optional next/rules/ifTrue+ifFalse/ranges describing what comes after.

Example: Score branching on the running total (Quiz only)

{
    "blockId": 1,
    "branchingType": "score",
    "scoreType": "total",
    "ranges": [
        {
            "min": 0,
            "max": 50,
            "blockId": 2
        },
        {
            "min": 51,
            "max": 100,
            "blockId": 3
        }
    ]
}

In spoken form: Show block ID 1. If the total score so far is 0-50 points, continue with block ID 2; if it is 51-100 points, continue with block ID 3.

scoreType: "total" is only available on a Quiz - Poll and Form have no running score to branch on.

Pitfalls

Building the logic settings can be tricky. Here are some tips to avoid common pitfalls:

  • Result blocks are not part of the logic tree in the API. Instead, they will always be shown at the end of the Riddle logic tree, regardless of the path taken. An end of the logic tree can be defined by adding a logic layer with no next property or with the next property set to null.
  • In a Poll or Form, every end of a path automatically leads to the first result page. In a Quiz, the result is picked by the minPercentage/maxPercentage ranges of your result pages instead, no matter which path the visitor took.
  • The same block may be referenced from several different branches - that is how two rules can converge on the same follow-up question (see the full example below, where both rules end on block 3). What is not allowed is referencing a block again below itself on the same path, which would create an infinite loop.
  • Make sure to use custom IDs for blocks and answers to make it easier to reference them in the logic settings.

Reading the logic back

When you read a Riddle back as a build configuration, the logic property behaves as follows:

  • A Riddle with the default linear logic does not return a logic property at all - leaving it out rebuilds exactly the same default.
  • Custom logic built through the API is returned in the same shape you sent (linear, answer, condition, or score - and any nested combination of them), and can be sent back unchanged. answers are always returned as answer IDs, never as answer texts.
  • Logic that was configured in the Creator but has no equivalent in a builder config - a condition builder rule targeting a data layer variable or magic variable, or logic referencing question bank / embedded form select blocks - cannot be expressed. In that case the whole logic property is reported as unsupported instead of being returned partially, because a partial tree could not be rebuilt into the same Riddle.

Changing the logic of an existing Riddle

When editing a Riddle, logic is replaced as a whole if you send it and kept as it is if you omit it - there is no per-node merge. Two edit-only rules apply:

  • Resetting: "logic": {"$reset": true} discards the custom logic and falls back to the default linear flow, regenerated from the current block order. It is exclusive - combining $reset with any other key inside logic is rejected, and it is only accepted when editing (never in a POST /riddle-builder request).
  • Deleting a referenced block: deleting a block that the custom logic still points at is rejected unless the same request either supplies a replacement tree or resets the logic. See RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_DELETED_BLOCK_STILL_REFERENCED below.

A Riddle whose logic was reset returns no logic property when read back, exactly like any other Riddle on the default flow.

Troubleshooting / Error codes

The logic settings throws unique error codes which can only occur in this context.

Not sure how exception handling works? Check out the basic API exception handling.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_CIRCULAR_REFERENCE

A circular reference was detected in the logic settings. This means that a block is referenced again going 'up' the logic tree, which would create an infinite loop.

Example faulty logic settings:

{
    "blockId": 1,
    "next": {
        "blockId": 2,
        "next": {
            "blockId": 1
        }
    }
}

In spoken form: Show block ID 1, then block ID 2, then block ID 1 again - which sends the visitor around the same two blocks forever.

The error message names the offending node and spells out the loop, so you do not have to walk your own configuration to find it:

Circular reference detected: logic (block ID 1) points back at block ID 1, which already appears earlier on this branch (1 -> 2 -> 1). Point it at another block, or end the branch instead.

The node is named the same way every other logic error names it - logic (block ID 1), plus its branchingType when the node states one, e.g. logic (block ID 1, branchingType: "answer"). The path in brackets reads in the direction you wrote it, from the block that first appears on the branch to the reference that closes the loop.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_INVALID_RULE

The answers provided in an answer branching rule object is either empty or references answers that do not exist in the corresponding block's items array.

Example faulty logic settings:

{
    "blockId": 1,
    "branchingType": "answer",
    "rules": [
        {
            "answers": [], // empty answers array
            "blockId": 2
        },
        {
            "answers": ["Non-existing answer"], // answer does not exist in block's items
            "blockId": 3
        }
    ]
}

In spoken form: the first rule says "if the user selects nothing in particular", and the second one branches on an answer block ID 1 does not offer - neither can ever trigger.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_NON_UNIQUE_RULES

The answers provided in an answer branching rule object are not unique. This means that the same answer is referenced in multiple rules of the same logic layer.

Example faulty logic settings:

{
    "blockId": 1,
    "branchingType": "answer",
    "rules": [
        {
            "answers": ["Answer 1", "Answer 2"],
            "blockId": 2
        },
        {
            "answers": ["Answer 2", "Answer 3"], // "Answer 2" is referenced twice
            "blockId": 3
        }
    ]
}

In spoken form: "Answer 2" would send the visitor to block ID 2 and to block ID 3 at the same time.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_NON_EXISTING_BLOCK

A blockId in the logic settings references a block that does not exist in the blocks array of the build configuration.

Example faulty logic settings:

{
    "blockId": 999, // block with ID 999 does not exist in blocks array
    "next": {
        "blockId": 2
    }
}

In spoken form: Start with a block that the Riddle does not contain, then show block ID 2.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_UNUSED_BLOCKS

Some blocks defined in the blocks array of the build configuration are not referenced in the logic settings. This means that there is no possible way for a user to reach these blocks when taking the Riddle. In the Riddle Creator this is indicated by a warning icon in the publish step, in the Builder API this results in an error to avoid building Riddles with unnecessary/unused blocks. The message lists the block IDs concerned: "The built logic tree does not use all Riddle blocks. Unused block IDs: 4, 5".

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_LINEAR_ONLY_ALLOWED

This can happen in two scenarios: you are trying to use non-linear (answer/condition/score) branching in a Quiz that contains Flashcard blocks - disallowed because Flashcards have complex logic themselves which does not work with non-linear logic - or you are trying to use non-linear branching on a Story Riddle, which has nothing to branch on.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_DEAD_ENDS

The built logic tree contains a dead end: a path that neither ends the flow nor leads anywhere. The message names the block it stops at, so you can go straight to that node:

The built logic tree contains a dead end at block ID 3: it neither ends the flow nor leads anywhere. Please check your logic configuration for that block.

Give that node a next - or, on a branching node, a target for every branch it can take.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_INVALID_NODE

A logic node is invalid. This can happen if the node is missing a blockId, if the blockId is not a positive integer, or if the branchingType is not a recognized value.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_CONDITION_INVALID_MAPPABLE

A condition branching node's condition.field does not refer to a usable value - for example it references a form field that doesn't exist on the blockId block, or a question block that carries no answer items/score to condition on.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_CONDITION_OPERATOR_NOT_ALLOWED

A condition branching node's condition.operator is not one of the operators allowed for the kind of field condition.field refers to. See the operator table under Condition branching above.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_SCORE_RANGE_OVERLAP

A score branching node's ranges contains two ranges that overlap, or a range whose min is greater than its max.

Example faulty logic settings:

{
    "blockId": 1,
    "branchingType": "score",
    "scoreType": "total",
    "ranges": [
        { "min": 0, "max": 50, "blockId": 2 },
        { "min": 40, "max": 100, "blockId": 3 }
    ]
}

In spoken form: a score of 45 points would match both ranges at once, so there is no telling whether block ID 2 or block ID 3 comes next.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_DELETED_BLOCK_STILL_REFERENCED

Only occurs when editing a Riddle: the request deletes a block ("$delete": true) that the Riddle's custom logic still references, and does not resolve that in the same request. The error names which deleted block is still referenced by which logic node.

Fix it by sending, in the same request, either a full replacement logic tree that no longer references the block, or "logic": {"$reset": true}. This never happens while the Riddle still uses the default linear logic - that logic is regenerated from the current block order on every build.

RIDDLE_BUILDER_BLOCK_PROPERTY_LOGIC_RESET_CONFLICT

"logic": {"$reset": true} was combined with other logic configuration in the same object. Either reset the logic or supply a full replacement tree, not both.

Answer branching step by step

Riddle build configuration (without logic settings in first step):

{
    "type": "Quiz",
    "build": {
        "title": "Logic example (Builder API)",
        "blocks": [
            {
                "id": 1,
                "title": "What's the capital of Germany?",
                "type": "SingleChoice",
                "items": [
                    { "title": "Berlin", "isCorrect": true },
                    { "title": "Lissabon", "isCorrect": false },
                    { "title": "Leipzig", "isCorrect": false }
                ]
            },
            {
                "id": 2,
                "title": "What are valid colors in German?",
                "type": "MultipleChoice",
                "items": [
                    { "title": "rot", "isCorrect": true },
                    { "title": "schwarz", "isCorrect": true },
                    { "title": "nero", "isCorrect": false }
                ]
            },
            {
                "id": 3,
                "title": "Order the following colors from lightest to darkest.",
                "type": "Order",
                "items": [
                    { "title": "red" },
                    { "title": "blue" },
                    { "title": "green" }
                ]
            }
        ],
        "results": [
            {
                "title": "Thanks for taking the quiz!",
                "minPercentage": 0,
                "maxPercentage": 100
            }
        ]
    }
}

The quiz above consists of three question blocks and one result block. For this example we want to branch based on the "What's the capital of Germany?" question. If the user answers "Berlin", we want to show the second question ("What are valid colors in German?"). If the user answers "Lissabon" or "Leipzig", we want to skip the second question and go directly to the third question ("Order the following colors from lightest to darkest.").

The logic tree / settings then look like this:

{
    "blockId": 1,
    "branchingType": "answer",
    "rules": [
        {
            "answers": ["Berlin"],
            "blockId": 2,
            "next": {
                "blockId": 3
            }
        },
        {
            "answers": ["Lissabon", "Leipzig"],
            "blockId": 3
        }
    ]
}

In spoken form: Show block ID 1 ("What's the capital of Germany?"). If the user answers "Berlin", show block ID 2 and then block ID 3. If the user answers "Lissabon" or "Leipzig", skip straight to block ID 3 and end there.

Note: Result blocks are not part of the logic tree in the API. Instead, they will always be shown at the end of the Riddle logic tree, regardless of the path taken. An end of the logic tree can be defined by adding a logic layer with no next property or with the next property set to null.

Optional: Referencing answers by ID

As with the blocks, the answer items can also be referenced by their ID instead of the answer text which makes it less confusing and more deterministic when creating logic rules for complex build configurations. Learn more

Step by step guide to convert from answer text to answer IDs:

  1. Add explicit IDs to the answers in the items property of the SingleChoice question block:
{
    "id": 1,
    "title": "What's the capital of Germany?",
    "type": "SingleChoice",
    "items": [
        { "id": 11, "title": "Berlin", "isCorrect": true },
        { "id": 22, "title": "Lissabon", "isCorrect": false },
        { "id": 33, "title": "Leipzig", "isCorrect": false }
    ]
}
  1. Use the answer IDs (i.e. 11, 22, 33) in the logic settings instead of the answer text:
{
    "blockId": 1,
    "branchingType": "answer",
    "rules": [
        {
            "answers": [11],
            "blockId": 2,
            "next": {
                "blockId": 3
            }
        },
        {
            "answers": [22, 33],
            "blockId": 3
        }
    ]
}

In spoken form: exactly the same flow as above - if the user picks answer 11 ("Berlin"), show block ID 2 and then block ID 3; if they pick answer 22 or 33, go straight to block ID 3.

Full example

Two complete request bodies that build the logic described above.

  1. A quiz that branches on the answer to its first question. Whoever answers "Berlin" also gets the colors question before the ordering question, whoever answers "Lissabon" or "Leipzig" goes straight to the ordering question, and both paths end on the same result page.
{
    "type": "Quiz",
    "build": {
        "title": "Logic example (Builder API)",
        "blocks": [
            {
                "id": 1,
                "title": "What's the capital of Germany?",
                "type": "SingleChoice",
                "items": [
                    { "title": "Berlin", "isCorrect": true },
                    { "title": "Lissabon", "isCorrect": false },
                    { "title": "Leipzig", "isCorrect": false }
                ]
            },
            {
                "id": 2,
                "title": "What are valid colors in German?",
                "type": "MultipleChoice",
                "items": [
                    { "title": "rot", "isCorrect": true },
                    { "title": "schwarz", "isCorrect": true },
                    { "title": "nero", "isCorrect": false }
                ],
                "explanation": {
                    "title": "Correct!",
                    "description": "Only 'rot' and 'schwarz' are valid colors in German"
                }
            },
            {
                "id": 3,
                "title": "Order the following colors from lightest to darkest.",
                "type": "Order",
                "items": [
                    { "title": "red" },
                    { "title": "blue" },
                    { "title": "green" }
                ],
                "itemsShuffled": true,
                "guesses": 3
            }
        ],
        "logic": {
            "blockId": 1,
            "branchingType": "answer",
            "rules": [
                {
                    "answers": ["Berlin"],
                    "blockId": 2,
                    "next": {
                        "blockId": 3
                    }
                },
                {
                    "answers": ["Lissabon", "Leipzig"],
                    "blockId": 3
                }
            ]
        },
        "results": [
            {
                "title": "Thanks for taking the quiz!",
                "minPercentage": 0,
                "maxPercentage": 100
            }
        ]
    }
}
  1. A poll that branches on a standalone form dropdown field. The visitor picks their favourite programming language and sees only the follow-up question for that language, Python leading to block ID 2, JavaScript to block ID 3 and Java to block ID 4, before the Riddle ends.
{
    "type": "Poll",
    "build": {
        "title": "Programming Language Quiz",
        "blocks": [
            {
                "id": 1,
                "title": "Select your favorite programming language:",
                "type": "FormField",
                "fieldType": "Dropdown",
                "items": [
                    { "title": "Python" },
                    { "title": "JavaScript" },
                    { "title": "Java" }
                ]
            },
            {
                "id": 2,
                "title": "Why do you like Python?",
                "type": "SingleChoice",
                "items": [
                    { "title": "Easy to learn" },
                    { "title": "Great libraries" },
                    { "title": "Versatile" }
                ]
            },
            {
                "id": 3,
                "title": "Why do you like JavaScript?",
                "type": "SingleChoice",
                "items": [
                    { "title": "Web development" },
                    { "title": "Versatile" },
                    { "title": "Large community" }
                ]
            },
            {
                "id": 4,
                "title": "Why do you like Java?",
                "type": "SingleChoice",
                "items": [
                    { "title": "Platform independence" },
                    { "title": "Strong typing" },
                    { "title": "Enterprise use" }
                ]
            }
        ],
        "logic": {
            "blockId": 1,
            "branchingType": "answer",
            "rules": [
                {
                    "answers": ["Python"],
                    "blockId": 2
                },
                {
                    "answers": ["JavaScript"],
                    "blockId": 3
                },
                {
                    "answers": ["Java"],
                    "blockId": 4
                }
            ]
        },
        "result": {
            "title": "Thanks for sharing your preferences!"
        }
    }
}