{
"data": {
"id": "<string>",
"surveyId": "<string>",
"surveyName": "<string>",
"workspaceId": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"finished": true,
"endingId": "<string>",
"language": "<string>",
"resolution": {
"labelPolicy": "currentSurveyDefinition",
"labelsLanguage": "<string>",
"surveyUpdatedAt": "2023-11-07T05:31:56Z"
},
"answers": [
{
"position": 2,
"elementId": "<string>",
"elementType": "openText",
"elementLabel": "<string>",
"valueText": "<string>",
"durationSeconds": 43200,
"inputType": "text"
}
],
"embeddedData": [
{
"key": "<string>",
"kind": "ingested",
"type": "string",
"label": "<string>",
"value": "<string>"
}
],
"unresolved": [
{
"key": "<string>",
"rawValue": "<string>",
"reason": "elementNotInSurvey"
}
],
"tags": [
{
"id": "<string>",
"name": "<string>"
}
],
"data": {},
"contact": {
"id": "<string>",
"userId": "<string>"
},
"displayId": "<string>",
"singleUseId": "<string>",
"durationSeconds": 1
}
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}Create a response
Records a survey response. Requires read-write access to the workspace, which is resolved from surveyId rather than supplied by the caller.
This performs a real submission. It runs the full response pipeline — webhooks, integrations, follow-ups, quota evaluation, and Hub ingestion once the response is finished — and it consumes one metered monthly response. That is the same power a v1/v2 management create has today, and the same power an API key with write access already carries; it is called out because an agent holding that key can create genuine submissions. Use POST /api/v3/responses/validate to check a payload without any of that happening.
createdAt cannot be supplied. Bulk historical loading is therefore not what this endpoint is for — see ResponseListItem.createdAt for why, and use v2 while a dedicated import path is outstanding.
{
"data": {
"id": "<string>",
"surveyId": "<string>",
"surveyName": "<string>",
"workspaceId": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"finished": true,
"endingId": "<string>",
"language": "<string>",
"resolution": {
"labelPolicy": "currentSurveyDefinition",
"labelsLanguage": "<string>",
"surveyUpdatedAt": "2023-11-07T05:31:56Z"
},
"answers": [
{
"position": 2,
"elementId": "<string>",
"elementType": "openText",
"elementLabel": "<string>",
"valueText": "<string>",
"durationSeconds": 43200,
"inputType": "text"
}
],
"embeddedData": [
{
"key": "<string>",
"kind": "ingested",
"type": "string",
"label": "<string>",
"value": "<string>"
}
],
"unresolved": [
{
"key": "<string>",
"rawValue": "<string>",
"reason": "elementNotInSurvey"
}
],
"tags": [
{
"id": "<string>",
"name": "<string>"
}
],
"data": {},
"contact": {
"id": "<string>",
"userId": "<string>"
},
"displayId": "<string>",
"singleUseId": "<string>",
"durationSeconds": 1
}
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}{
"title": "<string>",
"status": 123,
"detail": "<string>",
"requestId": "<string>",
"type": "<string>",
"instance": "<string>",
"code": "ai_features_not_enabled",
"details": {},
"invalid_params": [
{
"name": "<string>",
"reason": "<string>",
"code": "dangling_reference",
"identifier": "<string>",
"referenceType": "block",
"missingId": "<string>",
"firstUsedAt": "<string>",
"conflictsWith": "<string>"
}
]
}Authorizations
NextAuth session JWT cookie. Development: often next-auth.session-token.
Production (HTTPS): often __Secure-next-auth.session-token. Send the cookie your browser receives after sign-in.
Body
Creates a survey response. This is a real submission: it runs the full response pipeline — webhooks, integrations, follow-ups, quota evaluation, and Hub ingestion once the response is finished — and it counts against metered monthly responses, exactly as a v1/v2 management create does today. It is not an import primitive; a dedicated import path is future work.
The workspace is resolved from surveyId, so unlike POST /api/v3/surveys there is no workspaceId in the body — the caller cannot choose which scope it is authorized against.
Unsupported fields are rejected rather than ignored. Absent by design, each for its own reason:
-
createdAtandupdatedAtare server-owned — seeResponseListItem.createdAt. This is the one capability v3 does not carry over from v2, and it is why bulk historical loading still belongs on v2 until a dedicated import path exists. -
userIdis not accepted: a contactuserIdis a non-unique attribute, not a column, so it cannot safely identify who to attribute a response to. Link the contact explicitly withcontactId. -
contactAttributesis not accepted, and never was writable — v1/v2 snapshot it from the linked contact. v3 does not expose the snapshot at all;contactcovers the need. -
meta.ipAddress, and the deriveduserAgent/country/devicefamily, are not accepted; seeResponseMetaInputfor what is.
The survey to record the response against. Immutable after creation.
Whether this response is complete. Creating with finished: true fires the finish side of the pipeline immediately, including Hub ingestion.
The same map on the way in, with the cardinality bounds a v3 write enforces.
At most 500 keys, and at most 1000 entries in a multi-select array or matrix object. Both sit far above any real survey — a response holds one entry per element, a multi-select answer one per choice — and exist because every later reader pays per entry: the serializer's per-answer mapping, export columns, and the positional probe the "Other" response filter emits. Exceeding either is a 400 from the request schema, not a 422.
Deliberately separate from ResponseDataMap, which the read returns. These bounds are v3-only — the v1 and v2 write paths store the same map without them — so a stored response may exceed them, and the read schema must not claim otherwise.
Show child attributes
Show child attributes
Embedded Data values to write, keyed by field name.
This is the write counterpart of embeddedData[] on the read, and it is keyed the way an author names a field rather than the way storage addresses it. A hidden field is stored under its name but a variable is stored under its cuid, and that split is an implementation detail of the storage layout — the server resolves a name to its storage key through the survey's Embedded Data links, so a caller never sees a cuid and never has to know which kind it is writing.
Which kinds are writable:
-
ingested(hidden fields) — accepted, through the same ingest contract the SDK uses: the name must be declared on the survey, the value is coerced to the field's declaredtype, alockedfield ignores the write, and the whole payload is bounded. A name the survey does not declare is a 422 rather than a silent drop — knowing whether a name is declared means loading the survey, and these operations route every failure that needs stored state to 422. -
computed(variables) — accepted, because a management caller correcting a response needs to, and v1 and v2 both allow it today. -
reserved— refused. Auto-captured context describes the submission environment, and a caller-supplied value would be fiction. The three exceptions aresource,urlandaction, whichResponseMetaInputaccepts for exactly the replay case.
Values follow their field's declared type. A field the payload omits is left as it is; this is a merge over named fields, not a replacement of the collection.
null clears a field. Omission and null therefore mean different things — leave it alone versus remove its value — which is the only reading that makes a merge-shaped map able to express deletion at all. A cleared field is then absent from embeddedData[] on the next read, exactly as one that was never written, and a field whose definition carries a defaultValue falls back to that default rather than disappearing.
An ambiguous name is refused. The read says key is unique per kind rather than across the collection, because a survey predating the reserved-name guard can hold a variable and a hidden field under one name. This map has no kind, so on such a survey a name cannot say which field it means — and guessing would write the caller's value into the wrong one. A name that matches more than one declared field is a 422 naming it, for the same reason as an undeclared one; rename one side on the survey to clear it. The set is finite, frozen and tracked, and nothing new can enter it.
Reserved names collide differently and are not ambiguous: reserved is not writable at all, so a name that matches both a declared field and a catalog entry resolves to the declared one.
At most 500 names. Every name that matches no declared field becomes its own invalid_params entry naming it, so without a cap a large body answers with a 422 several times its own size. Far above any real survey; a v3-only bound, as on data.
Show child attributes
Show child attributes
Per-element time-to-complete, keyed by element id, in milliseconds. Accepted on create so that a caller replaying a real submission can carry its timing; reads report the same information as durationSeconds in seconds, on each answer and summed at the top level.
This is client-reported telemetry, not a measurement the server can vouch for, so each value is clamped into [0, 86400000] (24 hours per element) rather than rejected — a noisy timing value should never cost you the response. Any finite value is therefore accepted, and none is declared out of range.
The number of entries is capped at 500, matching data: a response holds one timing per element, so anything beyond that is not telemetry the server can use. A map with more entries is rejected — maxProperties below is enforced during request validation, so the whole write fails rather than the excess being trimmed or stored.
Do not send a _total key. The server owns that bucket: it drops any _total you supply and writes its own when the response is finished, by summing the element entries. Sending one therefore changes nothing rather than skewing the stored total, and durationSeconds on the read is computed from the element entries regardless.
An omitted map is not the same as an empty one. Omitting ttc stores no timings at all and the response reports no durationSeconds; sending {} on a finished response stores a _total of zero, which reads as "completed instantly".
Show child attributes
Show child attributes
Submission context, on the narrow subset a caller can legitimately supply. A management-API create has no browser behind it, so unlike a client-side submission there is nothing for the server to auto-capture — if these are not sent they are simply absent, which is why the API accepts them at all rather than treating all context as server-derived.
Deliberately not accepted here, because the routes derive them from the request itself and a caller-supplied value would be fiction: country, userAgent (the browser/OS/device breakdown is nested under it, there is no top-level device key) and ipAddress. ipAddress is additionally never projected by v3 on read, in any view.
The stored meta is wider than this input. It also carries a block of browser-runtime context — pagePath, pageReferrer, the five utm* keys, the four screen/viewport dimensions, timezone and locale — which the SDK captures and which this endpoint does not currently accept, so sending one is a 400. That is a gap rather than a decision: an integration replaying a real submission can legitimately carry utmSource or pagePath, and those are live catalog entries that read back in embeddedData[]. Whether v3 create accepts them is open on the write ticket.
Reads surface what is stored as reserved entries in embeddedData[].
Show child attributes
Show child attributes
Tag ids to apply, so a tagged response can be created in one call rather than a create followed by a patch. Tags must already exist in the same workspace — create them via POST /api/v3/tags. Applied in the same transaction as the response.
100The ending card the respondent reached. Must exist in the survey definition.
Survey language code to stamp on the response. Must be a language the survey has enabled. Omit for single-language surveys.
Contact to attribute the response to. Create-only — a response cannot later be re-linked to a different contact. Must be a contact in the same workspace.
Display (survey impression) this response answers. Create-only. Each display can back at most one response.
Single-use link id, for surveys configured with single-use links. Create-only, and unique per survey. Bounded because the value is indexed with surveyId: an empty string would slip past the uniqueness pre-check and still be stored, and an oversize one overflows the index entry.
1 - 255Response
Response created successfully
A survey response in the detailed view, as returned by GET /api/v3/responses/{responseId} and by the write endpoints. Everything in ResponseListItem, plus the identity and provenance fields the list view omits.
It also carries data — the answers as stored, returned unchanged, so a client can read a response, edit an answer and send it straight back. Embedded Data is not echoed in stored form: the read's embeddedData[] and a request's embeddedData map already address the same fields by the same names, so a second raw map would only republish the storage layout. That is the property v1/v2 had for free by being symmetric, and the one OpenAPI 3.1.1 argues for directly: its "Validating readOnly and writeOnly" section notes that making clients strip fields before a write is "burdensome for clients, particularly when the JSON data is complex or deeply nested".
They are not the same view, and the differences are deliberate. A write accepts ingested and computed fields only: a reserved entry describes the submission environment, and being able to read one back is not a licence to author it. An unset field is omitted from the read while null on a write clears one, because a merge-shaped map has no other way to spell deletion. And a name the read returns twice under different kinds cannot be written at all — see ResponseEmbeddedDataInput, which refuses an ambiguous name rather than guessing.
Values themselves round-trip: within those two kinds every value the read emits is accepted verbatim on a write, and a date reads and writes as the same ISO-8601 string. So a read-edit-write cycle loses nothing except in two cases, both confined to rows v1 or v2 wrote. The first is the finite set of legacy surveys carrying one name as both a variable and a hidden field, where the write refuses the name outright. The second is a data map holding more keys than ResponseDataMapInput accepts: the stored map is unbounded and the write bound is not, so such a response cannot be patched here — and trimming it to fit is not a workaround, since data is replaced wholesale and an omitted key is a deletion.
Note which fields carry readOnly here and which do not, because it is the opposite of what an "echo" framing would suggest. readOnly means a value is managed exclusively by the server, so it marks the derived views — answers[], embeddedData[], unresolved[], resolution, the denormalized surveyName and workspaceId, the timestamps, durationSeconds and id. data is not read-only: it is exactly what the write endpoints accept, and leaving it unmarked is what makes this representation round-trippable rather than merely informative.
To correct one answer, read this resource, take data, change the entry you want and PATCH { "data": … }. Send only the fields you are changing — PATCH rejects unknown and non-patchable keys, so passing the whole representation back verbatim is not the intended flow; passing data back is.
Never exposed on this resource, in either view: the respondent's IP address, and the contactAttributes snapshot that v1/v2 return. That snapshot is copied from the contact at submission time and never updated after, so it is stale PII by construction — contact covers the legitimate need to know who answered.
Show child attributes
Show child attributes
Was this page helpful?