Responses
Responses are the submissions to a form. They’re nested under a form and require the
responses:read scope.
List responses
Section titled “List responses”GET /v1/forms/{formId}/responses| Query param | Description |
|---|---|
cursor | Pagination cursor from a previous nextCursor. |
limit | Page size, default 25, max 100. |
since | Only responses started at/after this ISO-8601 UTC timestamp. |
until | Only responses started at/before this timestamp. |
status | all (default), complete, or partial. |
curl "https://publicapi.shout.com/v1/forms/90751669-9ebc-4215-9321-538b6c9e29ac/responses?status=complete&limit=1" \ -H "Authorization: Bearer $SHOUT_API_KEY"{ "items": [ { "id": "B3BfRaEZijiL_I15DzKuxpuLz33wnLSMZ9-lBi59snI", "completed": true, "startedAt": "2026-07-11T11:10:00Z", "timeTakenSeconds": 42, "countryCode": "US", "contactId": null, "answers": [ { "questionId": 7441598, "values": [ { "optionId": "d820082d-d403-407e-b9ea-821e61f9ec13", "label": "Dashboard", "text": null, "number": 1, "date": null } ] }, { "questionId": 7441603, "values": [ { "optionId": null, "label": null, "text": "More integrations please", "number": null, "date": null } ] } ] } ], "nextCursor": "djE6OTIxOTA1"}To poll for new responses, save the newest startedAt you’ve seen and pass it as since on your
next run — don’t reuse the cursor for that (cursors walk a fixed snapshot, since fetches what’s
new).
Get a single response
Section titled “Get a single response”GET /v1/forms/{formId}/responses/{responseId}curl "https://publicapi.shout.com/v1/forms/90751669-9ebc-4215-9321-538b6c9e29ac/responses/B3BfRaEZijiL_I15DzKuxpuLz33wnLSMZ9-lBi59snI" \ -H "Authorization: Bearer $SHOUT_API_KEY"Returns a single response in the same shape as a list item. A response ID only resolves under the
form it belongs to — fetching it under a different form returns 404 not_found.
Response fields
Section titled “Response fields”| Field | Type | Notes |
|---|---|---|
id | string | Opaque response ID — safe to store, but don’t parse it. Not a UUID. |
completed | boolean | true if the respondent finished the form. |
startedAt | string | ISO-8601 UTC time the response began. |
timeTakenSeconds | integer | null | Seconds spent; null while a response is still partial. |
countryCode | string | null | Two-letter country code, if known. |
contactId | UUID | null | The linked CRM contact, if the respondent is identified. |
answers | array | One entry per answered question (see below). |
Answer value shapes
Section titled “Answer value shapes”Each entry in answers is one question, keyed by its integer questionId (matching the question
id from GET /v1/forms/{id}):
{ "questionId": 7441598, "values": [ /* one or more values */ ] }values holds one entry per selected option / sub-field. A value is a union — only the fields
relevant to the answer are populated, the rest are null:
| Field | Populated for | Example |
|---|---|---|
optionId | Choice selections | "d820082d-..." — the chosen option’s UUID |
label | Choice / scale answers | "Dashboard", or a scale point like "8" |
text | Free-text answers | "More integrations please" |
number | Numeric / scored answers | 4 (also carries the numeric value behind a scale point) |
date | Date answers | "2026-07-01" |
Because it’s a union, read defensively: a single-choice answer has optionId + label (+ a
number if the option is scored); a text answer has only text; an unanswered question is simply
absent from answers (empty answers are omitted, not returned as blank values).