Responses — browsing, CSV export, deletion
Responses in the workspace toolbar opens the responses browser for that questionnaire.
Browsing
Section titled “Browsing”Responses list newest-first, 25 per page, with columns for created date, external reference, status (Completed / In progress), pinned version, answered-question count, and uploaded-file count. Three filters:
- Status — all, in progress, or completed.
- Version — all, or one specific published version.
- Reference — exact match on the external reference (apply with Enter).
Clicking a row expands it: the response id, the completion timestamp, and
the full answers map as JSON — exactly what’s stored, keyed by question
code. Uploaded files appear as their reference objects (fileId,
fileName, size, contentType); the browser doesn’t currently offer a
download link, but the management API’s download endpoint serves any file
by id (see the management API reference).
Hard delete
Section titled “Hard delete”The delete button on each row is data-subject erasure: it permanently
removes the response document and deletes its uploaded files from storage
(the file index keeps tombstone rows, which contain metadata only — name,
size, type — not content). The confirmation requires typing DELETE:
This permanently deletes response
r_…and its 2 uploaded file(s) from storage. There is no undo — erasure means erasure.
If a storage deletion fails mid-cascade, the response is still removed and the hourly cleanup job retries the stranded file.
Deleting a response also invalidates any browser session that was resuming it — the respondent’s next visit starts fresh.
CSV export
Section titled “CSV export”Export CSV downloads the current filter set as a spreadsheet-ready file. The important design decision: an export covers one questionnaire version — the live version, unless you’ve set the version filter — because columns are derived from that version’s definition. Codes that existed in v1 but not v3 would otherwise produce a ragged, misleading sheet. Export per version and you always get coherent columns.
Details of the format:
- Meta columns first:
responseId,externalRef,status,versionNumber,createdAt,completedAt— then one column per question code, in the questionnaire’s reading order. - Address questions expand into per-sub-field columns
(
homeAddr.city,homeAddr.state, …) for their enabled sub-fields. - Checkbox selections and uploaded files join with
;(files export their names); toggles exporttrue/false; numbers export bare. - UTF-8 with a byte-order mark, so Excel opens it with correct accents.
- Formula-injection guarded: any text cell starting with
=,+,-or@is prefixed with'so a malicious respondent’s answer can’t execute as a spreadsheet formula. Numeric answers are exempt —-5stays a number.
A version with no responses exports a header-only file, which is a handy way to see the exact column layout before data arrives.
Retention
Section titled “Retention”In-progress responses accumulate indefinitely — abandoned drafts are not auto-deleted (orphaned files are; the answer documents are not). Until a retention setting ships, a Mongo one-liner covers it — for example, deleting abandoned drafts older than 90 days:
docker compose exec mongo mongosh forms_engine --eval \ 'db.responses.deleteMany({status:"IN_PROGRESS",updatedAt:{$lt:new Date(Date.now()-90*864e5)}})'