Outcome
Interpret per-field answer counts for one authorized form, including multi-select counting and internal submission reads.
GET /api/forms/{formId}/summary
Before you start
Use the original authorized identity and intended organization. Discover a canonical Forms workspace and bind it as separate workspace resource context. The published application grant is workspace:read; an organization id, form id or arbitrary workspaceId input is not a grant.
These are authenticated administrative reads, distinct from the public form-response route. User bearer requests resolve a required workspace with read access; API-key requests rely on Auth introspection of the same supplied resource binding and require that binding. Preserve any refusal rather than widening context.
Examples are complete source-derived synthetic responses, not captured production reads. No real form, respondent, answer, key material, consent, CRM, delivery, provider/model or notification operation was executed. Source-to-serving association and browser/UI proof remain separate.
Request and response
Action: app_topolo_forms.summary.get. Use {} for a list, or the discovered formId for a form/summary read; bind the authorized workspace separately. Validate current published input before the read. Fixture ids authorize no operation.
Requires a discovered formId in the original verified workspace. After reading the form, selected source loads all matching submissions ordered by submitted_at descending, unprotects their answers/respondent metadata/placement context, then calculates the summary. The returned summary does not expose those records, but computation is still a private-data read.
There is no LIMIT, date filter, cursor, sampled aggregate, durable report or snapshot isolation in this selected summary path. Its submissionCount is the loaded submission array length, which can differ from the separately queried form count under concurrent changes. Do not repeatedly request large summaries as a cost-free health probe.
Layout fields section,divider,content,image,page_break are excluded. For each remaining current form field, answeredCount counts submissions whose value is neither undefined,null nor empty string. An empty array, false or0 passes that filter; answeredCount is not necessarily a nonempty, visible or valid answer count.
For array answers, each entry increments its display-text bucket; answeredCount counts the submission once, not each selection. Non-array values increment one display bucket. Objects use source display formatting (country/name/date-range/address or JSON fallback), so different raw values can collapse into one text bucket. This is not unique people, deduplicated responses or legal consent analysis.
Only current form field definitions are summarized. Unknown/removed answer keys and layout fields are excluded, while current hidden or consent fields are not removed solely for their type. There is no visibility re-evaluation in summarizeForm. Counts may therefore describe more or less than a respondent’s visible UI.
Protection and consistency
The original organization and verified workspace scope every selected form and submission query. API-key/bearer handling and required workspace read access precede the concrete route permission check. A permitted read does not authorize edits, publishing, submission, export, delivery retry, erasure or key changes.
The selected source unprotects stored content under its environment-aware protection policy before mapping the response. A protection/key/dependency failure is not an empty result or permission to change environment policy, provision keys, migrate data or expose ciphertext as readable content. The guide does not certify production key health.
Reads can expose decrypted form text, field definitions and author metadata; summaries also load submission records internally. Keep actual returned content within its approved audience. Publishing this technical guide with fictional data does not make the application data public.
Independent form, count and submission reads are not a single cross-store transaction. A missing count row is converted to zero by source; zero by itself is not a protection, collection-health or completeness check. Shared runtime failures can differ from the concrete route errors below; no unique carrier is invented.
Complete worked responses
Multi-select selections differ from answered submissions
Complete synthetic, unrun HTTP200. The current form has one topics multi_select field. Exactly3 readable fictional submissions contain topics:[Docs,API], topics:[Docs], and no topics value respectively. Thus2 answered submissions produce3 selection increments: Docs2,API1. No actual answer or respondent record was read.
Input:
{
"formId": "forms_docs_fixture"
}
Response:
{
"summary": {
"submissionCount": 3,
"fields": [
{
"fieldId": "topics",
"label": "Topics",
"type": "multi_select",
"answeredCount": 2,
"counts": {
"Docs": 2,
"API": 1
}
}
]
}
}
Interpret submissionCount3, answeredCount2 and bucket sum3 separately; they are not interchangeable totals.
Existing form with no submissions
Complete synthetic, unrun HTTP200. The fictional form still exists with the topics field, while the successful submission query returns no rows. This is not a dependency failure converted to zero.
Input:
{
"formId": "forms_docs_fixture"
}
Response:
{
"summary": {
"submissionCount": 0,
"fields": [
{
"fieldId": "topics",
"label": "Topics",
"type": "multi_select",
"answeredCount": 0,
"counts": {}
}
]
}
}
Keep the current field definition and empty counts; do not submit a real answer merely to test nonzero output.
Form not found
Complete synthetic, unrun HTTP404 after successful auth, workspace and route permission checks; selected form lookup returns no matching row before loading submissions.
Input:
{
"formId": "forms_absent_docs_fixture"
}
Response:
{
"error": "not_found"
}
Reconcile the actual discovered form/workspace without probing other tenants.
Verify and recover
-
Compare the actual returned fields against the intended current form definition. Preserve request/context evidence because this summary carrier itself contains no form/workspace id.
-
Check submissionCount, each answeredCount and bucket counts using their different definitions. Do not expect array selection totals to equal answered submissions or infer deduplication, unique people, visibility compliance or fulfillment.
-
On an actual shared auth, protection or database failure preserve the request id, status and sanitized error. Do not interpret failure as an empty result, disable protection, migrate/rotate keys or run business actions as a read recovery.
These reads make no form or submission mutation and need no data rollback. A published guide is not a grant to operate on any sample or real record.
Refusals
403 / organization_required: Authenticate in the intended original organization; do not guess a tenant id or change context to force access.
400 / workspace_required: Supply a discovered authorized canonical workspace as separate resource binding; an action input id alone does not grant access.
403 / forbidden: The selected route permission check refused workspace:read. Preserve the actual denial and seek legitimate access from its owner.
404 / not_found: For a single-form/summary read, reconcile the discovered form id and authorized workspace. Do not probe another tenant.
Concrete route carriers are distinguished from generic action metadata and shared runtime errors. Invalid local input is a schema-validation refusal, not a captured server response.
Related actions
Change log
- 2026-10-21: Added private computation boundaries, unbounded submission reads, exact multi-select/empty-value/current-field semantics and complete illustrative populated/empty/not-found responses. All examples remain synthetic and unrun.