Questions Form
Present multiple clarifying questions as an interactive Telegram form using inline buttons.
- Rating
- 4.5 (100 reviews)
- Downloads
- 14,049 downloads
- Version
- 1.0.0
Overview
Present multiple clarifying questions as an interactive Telegram form using inline buttons.
✨Key Features
Compose the Form
Send the Header
Send Each Question
Send the Submit Button
Handle Callbacks
Use the Answers
Complete Documentation
View Source →
Questions Form
Present multiple clarifying questions as a Telegram inline-button form. All questions appear at once; the user answers in any order, then submits.
When to Use
- You need 2 or more clarifying questions answered before proceeding
- Questions have enumerable options (with optional free-text fallback)
- The channel is Telegram
Form Protocol
Step 1: Compose the Form
Define each question internally as an object:
{ id: "type", text: "What type of project?", options: ["Web App", "Mobile", "API"] }
{ id: "timeline", text: "What is your timeline?", options: ["This week", "This month", "No rush"] }
{ id: "budget", text: "Budget range?", options: ["< $1k", "$1k-5k", "$5k-10k", "> $10k"] }
Initialize internal tracking state:
form_state = { type: null, timeline: null, budget: null }
awaiting_freetext = null
form_submitted = false
Step 2: Send the Header
Send a plain message (no buttons) as the form introduction:
{
"action": "send",
"channel": "telegram",
"message": "**I have a few questions before we proceed.**\nPlease answer each one by tapping a button, then tap Submit when done."
}
Step 3: Send Each Question
For each question, send a separate message with inline buttons. Put selectable options in rows of 2-3 buttons. Always put "Other" on its own last row.
The callback_data must follow this convention: form:
Example:
{
"action": "send",
"channel": "telegram",
"message": "**1. What type of project is this?**",
"buttons": [
[
{ "text": "Web App", "callback_data": "form:type:web" },
{ "text": "Mobile", "callback_data": "form:type:mobile" },
{ "text": "API", "callback_data": "form:type:api" }
],
[
{ "text": "Other (type your answer)", "callback_data": "form:type:other" }
]
]
}
{
"action": "send",
"channel": "telegram",
"message": "**2. What is your timeline?**",
"buttons": [
[
{ "text": "This week", "callback_data": "form:timeline:this_week" },
{ "text": "This month", "callback_data": "form:timeline:this_month" },
{ "text": "No rush", "callback_data": "form:timeline:no_rush" }
],
[
{ "text": "Other (type your answer)", "callback_data": "form:timeline:other" }
]
]
}
{
"action": "send",
"channel": "telegram",
"message": "**3. Budget range?**",
"buttons": [
[
{ "text": "< $1k", "callback_data": "form:budget:lt_1k" },
{ "text": "$1k-5k", "callback_data": "form:budget:1k_5k" }
],
[
{ "text": "$5k-10k", "callback_data": "form:budget:5k_10k" },
{ "text": "> $10k", "callback_data": "form:budget:gt_10k" }
],
[
{ "text": "Other (type your answer)", "callback_data": "form:budget:other" }
]
]
}
Step 4: Send the Submit Button
After all questions, send the submit/cancel message:
{
"action": "send",
"channel": "telegram",
"message": "**When you've answered all questions above, tap Submit.**",
"buttons": [
[{ "text": "\u2713 Submit All Answers", "callback_data": "form:submit" }],
[{ "text": "\u2717 Cancel", "callback_data": "form:cancel" }]
]
}
Step 5: Handle Callbacks
When you receive a callback starting with form::
Regular option (form: where value is not other):
- Record the answer:
form_state[qid] = value - Acknowledge: send
"Got it --: "
form::other ):
- Send:
"Type your answer for:" - Set
awaiting_freetext = qid - The next plain text message from the user is their free-text answer
- Record:
form_state[qid] = - Acknowledge:
"Got it --: " - Clear
awaiting_freetext
form:submit):
- Check
form_statefor anynullvalues - If incomplete: send
"You still need to answer:- "
- If complete: set
form_submitted = trueand proceed with the collected answers
form:cancel):
- Discard
form_state - Send:
"Form cancelled. Let me know how you'd like to proceed."
Step 6: Use the Answers
Once submitted, reference the collected answers as structured data and proceed:
Collected: { type: "mobile", timeline: "End of March", budget: "1k_5k" }
Now continue with the original task using these clarified requirements.
Changing Answers
Users can click a different button for any question at any time before submitting. Simply overwrite the previous value and acknowledge the change:
"Updated --
Callback Data Convention
- All form callbacks must use the
form:prefix - Format:
form:: - Keep question IDs short (2-8 chars) — Telegram has a 64-byte callback_data limit
- Keep values short and use underscores instead of spaces
- The agent identifies form callbacks by checking if the incoming message starts with
form:
Button Layout Rules
- Maximum 2-3 buttons per row for clean rendering
- Keep button labels under 20 characters
- Use Title Case for option labels
- "Other" button always says:
"Other (type your answer)" - "Other" button always goes on its own last row
- Submit button includes checkmark:
"\u2713 Submit All Answers" - Cancel button includes X mark:
"\u2717 Cancel"
Edge Cases and Advanced Patterns
See references/form-patterns.md for:
- Handling timeouts and abandoned forms
- Dependent questions (show question B only after A is answered)
- Large option sets (>6 options)
- Multi-select questions (toggle pattern)
- Free-text disambiguation
- Resuming interrupted forms
Installation
openclaw install questions-form
💻Code Examples
form_submitted = false
### Step 2: Send the Header
Send a plain message (no buttons) as the form introduction:}
### Step 3: Send Each Question
For each question, send a **separate message** with inline buttons.
Put selectable options in rows of 2-3 buttons. Always put "Other" on its own last row.
The `callback_data` **must** follow this convention: `form:<question_id>:<value>`
Example:}
### Step 4: Send the Submit Button
After all questions, send the submit/cancel message:}
### Step 5: Handle Callbacks
When you receive a callback starting with `form:`:
**Regular option** (`form:<qid>:<value>` where value is not `other`):
- Record the answer: `form_state[qid] = value`
- Acknowledge: send `"Got it -- <question label>: **<chosen label>**"`
**"Other" option** (`form:<qid>:other`):
- Send: `"Type your answer for: <question text>"`
- Set `awaiting_freetext = qid`
- The **next plain text message** from the user is their free-text answer
- Record: `form_state[qid] = <user's text>`
- Acknowledge: `"Got it -- <question label>: **<user's text>**"`
- Clear `awaiting_freetext`
**Submit** (`form:submit`):
- Check `form_state` for any `null` values
- If incomplete: send `"You still need to answer: <list of unanswered questions>"`
- If complete: set `form_submitted = true` and proceed with the collected answers
**Cancel** (`form:cancel`):
- Discard `form_state`
- Send: `"Form cancelled. Let me know how you'd like to proceed."`
### Step 6: Use the Answers
Once submitted, reference the collected answers as structured data and proceed:{ id: "type", text: "What type of project?", options: ["Web App", "Mobile", "API"] }
{ id: "timeline", text: "What is your timeline?", options: ["This week", "This month", "No rush"] }
{ id: "budget", text: "Budget range?", options: ["< $1k", "$1k-5k", "$5k-10k", "> $10k"] }form_state = { type: null, timeline: null, budget: null }
awaiting_freetext = null
form_submitted = false{
"action": "send",
"channel": "telegram",
"message": "**I have a few questions before we proceed.**\nPlease answer each one by tapping a button, then tap Submit when done."
}{
"action": "send",
"channel": "telegram",
"message": "**1. What type of project is this?**",
"buttons": [
[
{ "text": "Web App", "callback_data": "form:type:web" },
{ "text": "Mobile", "callback_data": "form:type:mobile" },
{ "text": "API", "callback_data": "form:type:api" }
],
[
{ "text": "Other (type your answer)", "callback_data": "form:type:other" }
]
]
}{
"action": "send",
"channel": "telegram",
"message": "**2. What is your timeline?**",
"buttons": [
[
{ "text": "This week", "callback_data": "form:timeline:this_week" },
{ "text": "This month", "callback_data": "form:timeline:this_month" },
{ "text": "No rush", "callback_data": "form:timeline:no_rush" }
],
[
{ "text": "Other (type your answer)", "callback_data": "form:timeline:other" }
]
]
}{
"action": "send",
"channel": "telegram",
"message": "**3. Budget range?**",
"buttons": [
[
{ "text": "< $1k", "callback_data": "form:budget:lt_1k" },
{ "text": "$1k-5k", "callback_data": "form:budget:1k_5k" }
],
[
{ "text": "$5k-10k", "callback_data": "form:budget:5k_10k" },
{ "text": "> $10k", "callback_data": "form:budget:gt_10k" }
],
[
{ "text": "Other (type your answer)", "callback_data": "form:budget:other" }
]
]
}Tags
Quick Info
Ready to Install?
Get started with this skill in seconds
Related Skills
4claw
4claw — a moderated imageboard for AI agents.
Aap Passport
Agent Attestation Protocol - The Reverse Turing Test.
Adaptive Suite
A continuously adaptive skill suite that empowers Clawdbot.
Adversarial Prompting
Adversarial analysis to critique, fix.