Adaptive AI questions
Three opt-in features: one clarifying follow-up, answer classification and personalised copy. All have fallbacks and guardrails.
Turn it on
{ "settings": { "personalization": { "ai": { "enabled": true, "maxFollowUps": 3, "consentFieldId": "ai_consent" } } } }AI runs only when enabled is true and, if you name a consentFieldId, only for respondents who answered that consent field yes. It is blocked in protected data mode and when the organization has no AI credits. In every blocked or failed case the respondent sees what you wrote. Tokens are metered to the organization. The default model is claude-sonnet-5-5.
AI follow-up field
{ "id": "why_followup", "type": "ai_followup", "label": "Tell us more",
"afterFieldId": "goal",
"intent": "Find out which tool they use today for this goal.",
"tone": "friendly, brief",
"maxQuestionLength": 120,
"answerTypes": ["text", "choice"],
"fallbackQuestion": "Anything else we should know?",
"fallbackChoices": ["Spreadsheets", "Another tool", "Nothing yet"] }After the answer to afterFieldId the AI writes exactly one question, as text or as 2 to 8 choices. The server checks the result: length, a single question, an allowed answer type, and no sensitive topic or request for a secret. If the check fails, the call times out (6 seconds), the form's maxFollowUps is reached or AI is off, the fallback is asked instead. The question is stored once per response and the same one is returned on reload. The question and the answer are stored on the submission.
Classify an answer
{ "id": "goal", "type": "long_text", "label": "What do you want to do?",
"classify": { "variableId": "goal_category", "minConfidence": 0.5, "fallbackCategory": "other",
"categories": [ { "id": "analytics", "label": "Analytics" }, { "id": "automation", "label": "Automation" },
{ "id": "reporting", "label": "Reporting" }, { "id": "other", "label": "Other" } ] } }The category id goes into the variable goal_category (declare it in variables). Use it like any variable: in conditions to branch, in text with {goal_category}. The server classifies again on submit, so a client cannot choose its category. Results are cached per input. Below minConfidence the fallbackCategory is used, or the variable stays at its initial value.
Because it is a normal variable, map it as a PostHog person property in the form's analytics mapping (the same place as answers) and it is sent with the completion.
Personalised copy
{ "id": "welcome_q", "type": "short_text", "label": "What should we call your first project?",
"personalize": { "enabled": true, "maxLength": 90, "guidance": "Mention their role if known" } }Also available on endings. The rewrite uses the attributes and earlier answers. It is dropped in favour of your text when it is longer than maxLength, loses a {placeholder}, adds numbers, links or marketing claims. Results are cached per input hash.
Endpoint
curl -X POST https://YOUR_HOST/api/v1/public/forms/app-onboarding/ai \
-H "Content-Type: application/json" \
-d '{"action":"followup","fieldId":"why_followup","submissionId":"sub_1","resumeToken":"...","answers":{"goal":"build dashboards"}}'
# { "data": { "question": "Which tool do you use for dashboards today?", "kind": "choice", "choices": ["Spreadsheets","Another tool"], "fallback": false } }| action | Returns |
|---|---|
followup | question, kind, choices, fallback |
classify | variableId, category (null when nothing fits), confidence |
copy | A map of rewritten text by key: field:<id>, ending:<id>:title, ending:<id>:body |