Questionnaires

Guide each visitor to the answer that fits them.

Every answer carries weights towards one or more profiles. The profile with the highest total decides which result page is shown — a page built in the editor, like any other.

How it works

A questionnaire is made of four objects. Understand them, and you can build any questionnaire you like.

The test

Its title, its slug, its introduction page. It starts out inactive: a half-built questionnaire would give wrong results.

The profiles

The possible outcomes. Each one points to the page shown to whoever gets it, and can also be awarded by overall score.

The questions

Single or multiple choice, in order. An inactive question can still be edited, but is no longer asked.

The weights

Carried by each answer, towards one or more profiles. They are the only input to the calculation.

1 — Create the test

It starts out inactive. You switch it on once profiles, questions and weights are in place.

POST /api/agent/v1/questionnaires/creer/
{
"titre": "Which kind of support suits you?",
"slug": "which-support",
"description": "Eight questions, two minutes.",
"page_de_presentation": 118
}

→ 201 { "questionnaire": { "id": 4, "actif": false } }

2 — Declare the profiles

Each profile points to the page shown to whoever gets it. The first two are reached through weights; the third through an overall score, which catches the undecided.

POST /api/agent/v1/questionnaires/4/profils/
{ "nom": "Self-reliant", "slug": "self-reliant",
"description": "Knows where they are going, wants a tool.",
"page_de_resultat": 120 }

POST /api/agent/v1/questionnaires/4/profils/
{ "nom": "Supported", "slug": "supported",
"page_de_resultat": 121 }

POST /api/agent/v1/questionnaires/4/profils/
{ "nom": "Still thinking", "slug": "still-thinking",
"page_de_resultat": 122,
"attribue_par_score": true,
"score_min": 0, "score_max": 6 }

3 — Ask a question and set its weights

Weights are what the calculation is made of. One answer can feed several profiles with different strengths — which is what keeps a questionnaire from being black and white.

POST /api/agent/v1/questionnaires/4/questions/
{
"texte": "Where are you with your current website?",
"type": "single",
"rang": 1,
"reponses": [
{ "texte": "I don't have one yet",
"poids": { "supported": 3, "still-thinking": 2 } },
{ "texte": "I have one, it no longer suits me",
"poids": { "self-reliant": 2, "supported": 2 } },
{ "texte": "I have one and want to run it myself",
"poids": { "self-reliant": 4 } }
]
}

4 — A multiple-choice question

The weights of every ticked answer add up. Useful for questions about usage, where several answers can be true at once.

POST /api/agent/v1/questionnaires/4/questions/
{
"texte": "What do you need to manage? (several answers)",
"type": "multiple",
"rang": 2,
"reponses": [
{ "texte": "An online store",
"poids": { "self-reliant": 1, "supported": 2 } },
{ "texte": "Appointments",
"poids": { "supported": 2 } },
{ "texte": "Online courses",
"poids": { "supported": 3 } },
{ "texte": "None of these for now",
"poids": { "still-thinking": 3 } }
]
}

5 — Settle two profiles that are too close

When two profiles finish neck and neck, the heavier one is not always the right call. A combination describes that case: “if Self-reliant and Supported are within 2 points of each other, show Supported”.

POST /api/agent/v1/questionnaires/4/combinaisons/
{
"titre": "Self-reliant or supported",
"profil_parent": "self-reliant",
"profil_prefere": "supported",
"profil_alternatif": "self-reliant",
"criteres": [
{ "profil_a": "self-reliant",
"profil_b": "supported",
"comparaison": "~=",
"tolerance": 2 }
]
}

→ « ~= » without a tolerance is REFUSED: the
criterion would amount to strict equality

6 — Build the result page

The result page is an ordinary page. Tokens in it are replaced at render time with the values of that attempt. Nothing that identifies the visitor appears there: the address of a result travels by email.

PUT /api/agent/v1/blocs/604/contenu/
{
"type": "TEXT",
"texte": "<h1>[%profil%]</h1>
<p>[%profil_description%]</p>
<p>Your score: [%score%]</p>"
}

Available tokens:
[%profil%] name of the profile obtained
[%profil_description%] its description
[%profil_prefere%] profile chosen by a combination
[%score%] overall score
[%test%] title of the questionnaire

7 — Switch it on, and only then

Activation is a separate, deliberate step. An active questionnaire with incomplete weights raises no alarm: it answers — and it answers wrong.

PATCH /api/agent/v1/questionnaires/4/
{ "actif": true }

Full review before activation:
GET /api/agent/v1/questionnaires/4/
→ profiles, questions, answers, weights and
combinations, in a single call

What the API refuses

A badly built questionnaire throws no error: it answers, and it answers wrong. Three refusals close the cases the database would happily accept — a weight aimed at another test's profile, a reversed score range no value could ever fall into, and a closeness comparison with no tolerance.

Three forms, one engine

The calculation never changes — weights that add up — but there are three ways to draw a result from it. They can be combined: nothing stops you from awarding some profiles by weight and others by score band.

Guidance by profile

The profile with the highest total wins. This is the most common form: the point is not to grade the respondent but to steer them to the answer that concerns them. A sales qualification questionnaire, a guide to choosing between several offers.

POST /api/agent/v1/questionnaires/4/profils/
{ "nom": "Self-reliant", "slug": "self-reliant",
"page_de_resultat": 120 }

POST /api/agent/v1/questionnaires/4/questions/
{ "texte": "How do you work today?",
"type": "single",
"reponses": [
{ "texte": "On my own, with my own tools",
"poids": { "self-reliant": 4 } },
{ "texte": "With an agency",
"poids": { "supported": 4 } }
] }

→ the highest total decides which page is shown

Assessment by overall score

The total is compared against bands. The profile is no longer a category but a LEVEL: digital maturity, readiness, exposure to a risk. The bands must cover the whole range without overlapping — a score that falls into none of them would lead to no page at all.

POST /api/agent/v1/questionnaires/5/profils/
{ "nom": "Beginner level", "slug": "beginner",
"attribue_par_score": true,
"score_min": 0, "score_max": 10,
"page_de_resultat": 130 }

POST /api/agent/v1/questionnaires/5/profils/
{ "nom": "Advanced level", "slug": "advanced",
"attribue_par_score": true,
"score_min": 11, "score_max": 25,
"page_de_resultat": 131 }

→ a reversed range is REFUSED: no score
could ever reach it

Settling close profiles

When two totals are close, the higher one is not always telling the truth: a one-point gap out of twenty is noise, not a preference. A combination describes that case and imposes the profile to keep. That is what separates an honest questionnaire from one that decides by chance.

POST /api/agent/v1/questionnaires/4/combinaisons/
{
"titre": "Self-reliant or supported",
"profil_parent": "self-reliant",
"profil_prefere": "supported",
"profil_alternatif": "self-reliant",
"criteres": [
{ "profil_a": "self-reliant",
"profil_b": "supported",
"comparaison": "~=",
"tolerance": 2 }
]
}

Comparisons: > < >= <= == ~=
« ~= » requires a tolerance — without one, the
criterion would amount to strict equality,
and would never settle anything

Question types

There are two, and the choice is not cosmetic: with multiple choice, the weights of every ticked answer add up, which changes the scale of the totals. A questionnaire that mixes both needs weights calibrated accordingly.

  1. single — one answer only. That answer's weights are added
  2. multiple — several answers. Each one's weights add up, so a question's total can exceed that of a single-choice question

An inactive question can still be edited but is no longer asked. That is how you remove a question from a live questionnaire without skewing the attempts already recorded.