Inserting Responses

Use the SmartSurvey API to add completed, partial, or disqualified responses to a survey.

Use the API to add responses to a survey — completed, partial, or disqualified — just as if someone had filled it in. This is useful for integrations that collect answers elsewhere, and for importing historical responses.

Endpoint

POST {base_url}/surveys/{surveyId}/responses
AuthenticationBasic auth — Authorization: Basic base64(api_token:api_secret)
PermissionYour API key must have permission to add responses to the survey
BodyJSON (application/json), or multipart/form-data when the survey has file-upload questions
Query?return=full | minimal | none (default minimal)

Request body

FieldTypeDescription
statusstring (required)completed, partial, or disqualified
questionsarrayThe answers, grouped by question (see below)
variablesarray{ "name", "value" } pairs to store against the response, matched to the survey's variables
completion_actionsstringFor completed responses, which follow-up actions to run: all (default), no_email (everything except notification emails), or none (record the response only — no emails, webhooks, integrations or AI). Use none when importing
tracking_link_idintegerAttribute the response to a specific collector/link; omit for the survey default
translation_idintegerThe survey's language/translation id; omit to use the default
unique_idstringYour own unique reference for the response
date_started / date_endeddate-timeWhen the response was started/finished; set these when importing past responses
generate_save_and_continue_linkbooleanFor a partial response, return a link to resume it later

Supplying answers

Each item in questions is { "id": <questionId>, "answers": [ … ] }. The fields you set on each answer depend on the question type:

Question typeHow to answer
Radio, dropdown, list, single image, CSAT, CES, FFT, EES, ESAT, ENPSone answer: choice_id
Checkbox / multi-selectone answer per selected option (choice_id each)
An “other” optionset choice_id for the option and value for the typed-in text
Single text box, essay, single dateone answer: value only
Multiple text boxes, multi-date range, AI Follow-Upone answer per input: choice_id (the input) and value
Matrix (radio/checkbox/text/dropdown)one answer per cell: row_id and column_id (plus value for text grids)
NPS / ratingone answer: choice_id (the chosen point)
Constant sum, sliderone answer per row: choice_id and value (the number)
Date boxvalue in the question's format: dd/MM/yyyy, HH:mm (24-hour), or dd/MM/yyyy HH:mm (MM/dd/yyyy for US date order)
Rankingone answer per ranked option (choice_id)
Mappins: [{ "latitude", "longitude", "comment"? }]
File uploaduse the multipart request (see below)

Examples

Add a completed response

POST {base_url}/surveys/1718994/responses
Authorization: Basic ZXhhbXBsZV90b2tlbjpleGFtcGxlX3NlY3JldA==
Content-Type: application/json
{
  "status": "completed",
  "questions": [
    { "id": 26210001, "answers": [ { "choice_id": 5500001 } ] },
    { "id": 26210002, "answers": [
        { "choice_id": 5500010 },
        { "choice_id": 5500012 },
        { "choice_id": 5500099, "value": "Heard from a friend" }
    ] },
    { "id": 26210003, "answers": [ { "value": "Loved the onboarding flow." } ] },
    { "id": 26210004, "answers": [ { "choice_id": 5500030 } ] }
  ]
}

A radio question, a checkbox question (two options plus an “other” with text), a single text box (value only), and an NPS question.

Response — 201 Created

{ "id": 88123456 }

with header Location: /surveys/1718994/responses/88123456.

Partial response with a resume link

{
  "status": "partial",
  "generate_save_and_continue_link": true,
  "questions": [
    { "id": 26210001, "answers": [ { "choice_id": 5500001 } ] }
  ]
}

Import a historical response

{
  "status": "completed",
  "completion_actions": "none",
  "date_started": "2025-11-02T09:14:00Z",
  "date_ended":   "2025-11-02T09:19:30Z",
  "unique_id": "crm-12345",
  "questions": [ { "id": 26210001, "answers": [ { "choice_id": 5500001 } ] } ]
}

completion_actions: none records the response without sending emails or triggering webhooks, integrations or AI. Use no_email instead if you still want webhooks and integrations to run.

Store variables

{
  "status": "completed",
  "questions": [ /* … */ ],
  "variables": [
    { "name": "source", "value": "api-import" },
    { "name": "tier",   "value": "enterprise" }
  ]
}

What gets returned

Every successful insert returns 201 Created with a Location header pointing at the new response. The body depends on ?return=:

returnBody
minimal (default){ "id": <responseId> }
fullthe full response, with pages, questions and answers
noneempty — read the id from the Location header

Status codes

CodeMeaning
201The response was added
400The request was invalid — check the returned error details (e.g. an answer that breaks the question's format, length, selection or total rules)
403Your API key can't add responses to this survey
404The survey doesn't exist
409The response couldn't be added (for example, your account's response allowance has been reached, or an attached file couldn't be saved). Nothing was stored — you can safely retry

Good to know

  • A completed response counts towards your account's response allowance.
  • Only the answers you send are validated (format, range, selection, totals). “Required” rules aren't enforced, so partial responses and imports are accepted.
  • translation_id is the survey's numeric translation id, not a language code (e.g. en).
  • For map questions, send pins instead of the usual answer fields.
  • Selection is expressed only by including a choice_id in the answers array — there's no is_selected field to set.
  • Sending the same choice_id twice, or the same row_id + column_id pair twice, returns a 400 rather than being silently deduplicated.
  • A 403 doesn't only mean a permissions problem — it's also returned if your account's plan doesn't include response inserts via the API at all. The error message tells you which case applies; contact SmartSurvey Support if you hit the plan-tier one.

Question type examples

Dropdown, list, single image, and the score/rating variants (CSAT, CES, eNPS, star/smiley/thumb ratings) all use the same shape as the Radio example above — one answer, choice_id only.

Matrix, constant sum and slider

{
  "status": "completed",
  "questions": [
    { "id": 26210010, "answers": [
        { "row_id": 5501001, "column_id": 5501010 },
        { "row_id": 5501002, "column_id": 5501012 }
    ] },
    { "id": 26210011, "answers": [
        { "choice_id": 5502001, "value": "60" },
        { "choice_id": 5502002, "value": "40" }
    ] },
    { "id": 26210012, "answers": [ { "choice_id": 5503001, "value": "7" } ] }
  ]
}

Matrix — checkbox, text grid and dropdown cells

The example above shows a single-answer-per-row matrix. Checkbox matrices work the same way but allow more than one column_id per row_id; text-grid and dropdown matrices add a value:

{
  "status": "completed",
  "questions": [
    { "id": 26210016, "answers": [
        { "row_id": 5507001, "column_id": 5508001 },
        { "row_id": 5507001, "column_id": 5508002 },
        { "row_id": 5507002, "column_id": 5508010, "value": "Great support team" },
        { "row_id": 5507003, "column_id": 5509005, "value": "5509005" }
    ] }
  ]
}

Row 5507001 is a checkbox row with two columns ticked. Row 5507002 is a text-grid cell — value is the typed text. Row 5507003 is a dropdown-matrix cell — value is the chosen option's id, not its display text.

Ranking

{
  "status": "completed",
  "questions": [
    { "id": 26210013, "answers": [
        { "choice_id": 5504001 },
        { "choice_id": 5504002 },
        { "choice_id": 5504003 }
    ] }
  ]
}

Rank is the position in the array, not a field you send — 5504001 is ranked 1st, 5504002 2nd, and so on. There's no explicit rank number.

Multiple textboxes, multi-date range and AI Follow-Up

For a question with more than one input (e.g. "First name" / "Last name", or a date range), each input is its own answer: choice_id identifies which input, value is what was typed.

{
  "status": "completed",
  "questions": [
    { "id": 26210014, "answers": [
        { "choice_id": 5505001, "value": "Alice" },
        { "choice_id": 5505002, "value": "Smith" }
    ] }
  ]
}

An AI Follow-Up question uses the same shape — the initial answer, then each generated follow-up question/answer pair, all keyed by their own choice_id:

{
  "status": "completed",
  "questions": [
    { "id": 26210015, "answers": [
        { "choice_id": 5506001, "value": "I loved the onboarding flow" },
        { "choice_id": 5506002, "value": "Why do you say that?" },
        { "choice_id": 5506003, "value": "Because it only took two minutes" }
    ] }
  ]
}

Comment box

A comment box can be attached to almost any question type. It's just another answer cell, identified by the comment option's choice_id, alongside the question's normal answer(s):

{
  "status": "completed",
  "questions": [
    { "id": 26210017, "answers": [
        { "choice_id": 5510001 },
        { "choice_id": 5510002, "value": "Extra thoughts here" }
    ] }
  ]
}

Date box

value must match the question's configured format:

{
  "status": "completed",
  "questions": [
    { "id": 26210018, "answers": [ { "value": "25/12/1990" } ] }
  ]
}
  • Date only: dd/MM/yyyy (or MM/dd/yyyy for US-format surveys) — e.g. "25/12/1990"
  • Time only: HH:mm (24-hour) — e.g. "14:30"
  • Date and time: dd/MM/yyyy HH:mm — e.g. "25/12/1990 14:30"

Map question

{
  "status": "completed",
  "questions": [
    { "id": 26214630, "answers": [ {
        "pins": [
          { "latitude": 51.5074, "longitude": -0.1278, "comment": "Head office" },
          { "latitude": 53.4808, "longitude": -2.2426 }
        ]
    } ] }
  ]
}

Latitude must be between −90 and 90, longitude between −180 and 180.

File uploads (multipart)

Send the same JSON in a payload form field, and attach each file as a part named question_<questionId> (repeat the name for multiple files on one question):

POST {base_url}/surveys/1718994/responses
Authorization: Basic ZXhhbXBsZV90b2tlbjpleGFtcGxlX3NlY3JldA==
Content-Type: multipart/form-data; boundary=----X
------X
Content-Disposition: form-data; name="payload"

{"status":"completed","questions":[{"id":26210020,"answers":[]}]}
------X
Content-Disposition: form-data; name="question_26210020"; filename="cv.pdf"
Content-Type: application/pdf

%PDF-1.7 ...binary...
------X--