Form Responses
Viewing Responses
Use list-responses to see submissions for a specific form:
pnpm action list-responses --form <form-id> [--limit 50]
This shows each response with field labels and values, ordered by submission date (newest first).
For chart/table analytics, prefer response-insights:
# All recent accessible forms
pnpm action response-insights
# One form
pnpm action response-insights --formId <form-id> --days 30 --limit 300
response-insights returns an explicit first-party widget payload:
widget: "data-insights"chartSeriesfor submissions by daytablefor recent response rowssummarywith total, sampled, and truncation details
Native chat renderers should use that contract for first-party tables/charts. MCP Apps/iframe rendering is only a fallback for external hosts.
For form setup/configuration previews, use preview-form:
pnpm action preview-form --formId <form-id>
It returns a native inline summary/table with the form fields, response count, status, visibility, and an "Open editor" action.
Exporting Responses
Use export-responses to export to CSV or JSON. The export is uploaded to
configured file storage (never written to local disk — serverless hosts have
a read-only filesystem) and the action returns the resulting file URL:
# CSV export (default)
pnpm action export-responses --form <form-id>
# JSON export
pnpm action export-responses --form <form-id> --format json
The CSV includes headers derived from field labels. Array values (multiselect) are joined with semicolons.
Response Data Structure
Each response is stored in the responses SQL table:
| Column | Type | Description |
|---|---|---|
id |
text | Unique response ID |
formId |
text | Foreign key to the form |
data |
text | JSON string of field ID -> value map |
submittedAt |
text | ISO timestamp |
ip |
text | Submitter IP when available |
submitterEmail |
text | Submitter email hint when known |
pageUrl |
text | Page the respondent was on, if sent |
clientSurface |
text | App surface: web, electron, or tauri |
submitterEmail may come from the logged-in Forms session or from trusted
feedback clients that pass the logged-in user email as submission metadata.
pageUrl and clientSurface are hidden pass-through fields. Direct public
fills record the current form URL, while trusted embeds (e.g. the framework
FeedbackButton) forward the respondent's source page and runtime shell (web,
electron, or tauri) so owners can see which screen and app feedback came
from. Sensitive URL query keys are scrubbed before persistence, clientSurface
is allowlisted server-side (unknown values are dropped), and anonymous forms
suppress both fields. The responses table surfaces them as "Page" and "Source"
columns when any response carries them.
The data JSON maps field IDs to values:
{
"name": "Alice Smith",
"email": "alice@example.com",
"rating": 5,
"interests": ["design", "development"]
}
Analyzing Responses
To analyze responses, the workflow is:
list-formsto find the form IDpreview-form --formId <id>when the question is about setup or fieldsresponse-insights --formId <id>for counts, daily submissions, and table data- Use
list-responses --formId <id>only when exact row-level inspection is needed - Report whether the answer is exact or sampled, including row counts and truncation
Common Tasks
| User request | What to do |
|---|---|
| "@Form setup?" | preview-form --formId <id> and answer from the returned fields/settings |
| "How many responses?" | response-insights --formId <id> and report summary.responses |
| "Export to CSV" | export-responses --form <id> |
| "Submissions by day" | response-insights --formId <id> --days 30 |
| "Summarize feedback" | response-insights, then list-responses if more detail is needed |
| "Average rating" | list-responses, compute from rating fields and state the sampled row count |
| "Who submitted today?" | list-responses, filter by submittedAt |
Related Skills
- form-building — Understanding the form structure and field types
- actions — All response operations go through actions