Imports API
Endpoints that import existing questionnaires from Creative Survey (CS). They are called by the Chrome extension.
Imported data is stored as-is in the external data tables (external_*), and each question is also registered into the question library as "external-origin data".
See API Definition for the response format and error handling.
Method
HTTP Methods
| Method | URI | Overview |
|---|---|---|
| POST | /api/imports/creative-survey/bundle |
Import a Creative Survey bundle |
| GET | /api/imports/creative-survey/existing-ids |
List already-imported externalIds |
| GET | /api/imports/creative-survey/section-previews |
Section candidate preview (all) |
| GET | /api/imports/creative-survey/section-previews/{externalSurveyId} |
Section candidate preview (single) |
Authentication
/api/imports/* accepts API key authentication in addition to the usual user-header authentication.
| Method | Condition |
|---|---|
| User headers | x-user-email + x-user-name |
| API key | SURVEY_IMPORT_API_KEY is set and the x-api-key header matches it |
With API key authentication, the import is recorded as the user given in x-user-email, or as import-bot@local (display name Import Bot) when no user is given.
Import a Creative Survey Bundle
Overview
Receives the questionnaire data (bundle JSON) that the Chrome extension fetched from the CS API and stores it in the external data tables.
If the same questionnaire (the source + externalId pair) already exists, it is cascade-deleted and re-inserted. Repeated imports therefore never produce duplicates.
URI
Request Headers
Content-Type:application/jsonx-api-key:<SURVEY_IMPORT_API_KEY>(for API key authentication)
Request Body
{
"fetchedAt": "2026-08-18T00:00:00.000Z",
"origin": "https://4dsd.svy.ooo",
"summary": {},
"surveys": [
{
"surveyId": 12345,
"surveyMeta": {},
"survey": { "name": "ๆขๅญใฎ่ชฟๆป็ฅจ" },
"questionnaire": { "id": 1, "survey_id": 12345, "generate_order": [[101, 102]] },
"questions": [
{
"id": 101,
"answer_type": 2,
"answer_type_name": "้ธๆ",
"rendered_sentence": "ใใชใใฎๆงๅฅใใ็ญใใใ ใใใ",
"order_index": 0,
"any_logic": true,
"any_visibility": false,
"answer_items": [{ "id": 1001, "sentence": "็ทๆง", "order_index": 0 }],
"sub_items": [],
"logics": [
{
"id": 5001,
"order_index": 0,
"logic_items": [
{ "id": 9001, "question_id": 101, "answer_item_id": 1001, "verb": 0, "value": "" }
],
"logic_action": { "id": 7001, "question_id": 105, "is_random": false }
}
]
}
],
"errors": []
}
]
}
Validation Rules
| Field | Rule |
|---|---|
| origin | Required. A string of at least one character |
| surveys | Required. An array with at least one entry |
| surveys[].surveyId | Required. Number (the CS questionnaire ID) |
| surveys[].survey | Required. An object or null |
| surveys[].questionnaire | Required. An object or null |
| surveys[].questions | Optional. Array of questions |
| fetchedAt / summary / errors | Optional |
Question, choice, and branch objects pass unknown fields through (because the CS API is undocumented, unexpected fields are still stored as raw JSON).
Response (200 OK)
{
"ok": true,
"data": {
"surveysImported": 1,
"questionsImported": 24,
"answerItemsImported": 130,
"subItemsImported": 12,
"logicsImported": 8,
"logicItemsImported": 15,
"libraryItemsImported": 24,
"surveyIds": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"],
"skipped": []
}
}
| Field | Type | Notes |
|---|---|---|
| surveysImported | number | Number of questionnaires imported |
| questionsImported | number | Number of questions imported |
| answerItemsImported | number | Number of choices imported |
| subItemsImported | number | Number of sub items imported |
| logicsImported | number | Number of branch logics imported |
| logicItemsImported | number | Number of branch conditions imported |
| libraryItemsImported | number | Number of entries registered into the question library |
| surveyIds | string[] | UUIDs of the stored external_surveys rows |
| skipped | object[] | Questionnaires that were not imported (externalId and reason) |
Error Handling
| Description | Status code | Status name |
|---|---|---|
Malformed request body (empty surveys, empty origin, โฆ) |
400 | Bad Request |
| Missing authentication, or a mismatched API key | 401 | Unauthorized |
Processing Flow
Sequence Diagram
sequenceDiagram
participant Ext as Chrome extension
participant API
participant DB
participant Library as Question library
Ext->>API: POST /api/imports/creative-survey/bundle
API->>API: Validate the request
API->>API: Resolve the actor (Import Bot under API key auth)
loop For each questionnaire
API->>DB: Look up an existing (source, externalId)
opt Exists
API->>DB: Delete the existing external_surveys row (children cascade)
end
API->>DB: Store into external_surveys
API->>DB: Store into external_questions
API->>DB: Store into external_answer_items / external_sub_items
API->>DB: Store into external_logics / external_logic_items
API->>Library: Register each question as external-origin data
end
API->>API: Aggregate the import counts
API-->>Ext: 200 OK
List Already-imported externalIds
Overview
Returns the list of CS questionnaire IDs that have already been imported.
The Chrome extension uses this to skip already-fetched questionnaires when resuming an interrupted import.
URI
Query Parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| source | string | Source identifier. Defaults to creative_survey |
Response (200 OK)
Error Handling
| Description | Status code | Status name |
|---|---|---|
| Missing authentication | 401 | Unauthorized |
Section Candidate Preview (All)
Overview
Returns section candidates for every imported external questionnaire.
CS has no concept of sections, so the question sequence is split into section candidates at questions whose answer_type_name is "ๆ็คบในใใใ" (intro step).
URI
Response (200 OK)
{
"ok": true,
"data": [
{
"externalSurveyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"externalSurveyName": "ๆขๅญใฎ่ชฟๆป็ฅจ",
"externalSurveyExternalId": 12345,
"sections": [
{
"index": 0,
"title": "ในใฏใชใผใใณใฐ",
"description": "",
"libraryItemIds": ["โฆ", "โฆ"],
"questionCount": 5
}
]
}
]
}
| Field | Type | Notes |
|---|---|---|
| sections[].index | number | Position of the section candidate |
| sections[].title | string | Title derived from the text of the intro step that acted as the divider |
| sections[].libraryItemIds | string[] | UUIDs of the question library entries in this section |
| sections[].questionCount | number | Number of questions |
Passing the resulting libraryItemIds to Insert One Section from External Entries inserts them as a single section.
Error Handling
| Description | Status code | Status name |
|---|---|---|
| Missing authentication | 401 | Unauthorized |
Section Candidate Preview (Single)
Overview
Returns the section candidates of a single external questionnaire.
URI
| Parameter | Type | Required | Notes |
|---|---|---|---|
| externalSurveyId | string | โฏ | UUID in external_surveys |
Validation Rules
| Field | Rule |
|---|---|
| externalSurveyId | Required. UUID format |
Response (200 OK)
Returns the section candidates of one questionnaire (the structure matches an element of the list version).
Error Handling
| Description | Status code | Status name |
|---|---|---|
externalSurveyId is not a UUID |
400 | Bad Request |
| The external questionnaire does not exist | 404 | Not Found |