API Reference
Complete reference for the Qlarr backend REST API. There is no global route prefix — all endpoints are at the root level (no /api/ prefix) and routes are unversioned.
For authentication details, see Authentication.
Roles
Role names as used in the codebase: super_admin, survey_admin, surveyor, analyst.
A global JwtAuthGuard protects every route unless it is marked @Public(); a RolesGuard enforces @Roles(...). "Auth: none" below means the route is public. "Auth: authenticated" means any valid token is accepted regardless of role.
Health
GET /health
Liveness check — the app is up and serving HTTP.
- Auth: none
GET /health/db
Readiness check — proves the app can reach PostgreSQL.
- Auth: none
Authentication
POST /user/login
Authenticate with email and password. Returns an access token + refresh token.
- Body:
{ email, password } - Auth: none
- Rate limit: 10 attempts / 5 min per email
POST /user/refresh_token
Refresh an expired access token. Refresh tokens are not rotated.
- Body:
{ accessToken, refreshToken } - Auth: none
POST /user/forgot_password
Send a password reset email (PIN to the registered address).
- Body:
{ email } - Auth: none
- Rate limit: 5 attempts / 15 min per email
POST /user/reset_password
Reset a password using the reset token from the email.
- Body:
{ refreshToken, newPassword } - Auth: none
POST /logout
Log out and invalidate the current session.
- Auth: authenticated
GET /auth/me
Verify the current JWT and return its decoded principal.
- Auth: authenticated
Users
GET /users/me
Get the current user's profile.
- Auth: authenticated
GET /user/all
List all users.
- Auth: super_admin, survey_admin
GET /user/count_by_role
Get user counts grouped by role.
- Auth: super_admin
POST /user/create
Create a new user.
- Body:
{ firstName, lastName, email, roles } - Auth: super_admin
GET /user/{userId}
Get user details by ID.
- Auth: authenticated
PUT /user/{userId}
Update a user (name, roles).
- Body:
{ firstName?, lastName?, roles? } - Auth: super_admin
DELETE /user/{userId}
Delete a user.
- Auth: super_admin
PUT /user/profile
Update own profile.
- Body:
{ firstName?, lastName?, email?, password?, newPassword? } - Auth: authenticated
- Rate limit: 5 attempts / 15 min per user (for email/password changes)
POST /user/confirm_new_email
Confirm an email-address change using a PIN.
- Body:
{ pin } - Auth: authenticated
- Rate limit: 5 attempts / 15 min per user
Surveys
GET /survey/all
List all surveys with pagination and filtering.
- Query:
page,per_page,sort_by,status - Auth: authenticated
GET /survey/offline
Get surveys available for offline mode.
- Auth: super_admin, survey_admin, surveyor
POST /survey/create
Create a new survey.
- Body:
{ name, usage? }(usage:web|offline|mixed) - Auth: super_admin, survey_admin
POST /survey/import
Import a survey from an export ZIP.
- Body: multipart file upload (
file) - Auth: super_admin, survey_admin
GET /survey/{surveyId}
Get survey metadata.
- Auth: authenticated
PUT /survey/{surveyId}
Update survey metadata (name, dates, quota, privacy, navigation, etc.).
- Body: survey settings
- Auth: super_admin, survey_admin
PUT /survey/{surveyId}/close
Close/deactivate a survey.
- Auth: super_admin, survey_admin
POST /survey/{surveyId}/clone
Clone an existing survey.
- Auth: super_admin, survey_admin
GET /survey/{surveyId}/export
Export a survey as a ZIP package.
- Auth: super_admin, survey_admin
DELETE /survey/{surveyId}
Delete a survey.
- Auth: super_admin, survey_admin
Survey Design
GET /survey/{surveyId}/design
Get the current survey design (editor input + version metadata).
- Auth: super_admin, survey_admin
POST /survey/{surveyId}/design
Save/update the survey design.
- Query:
version - Body: survey design JSON
- Auth: super_admin, survey_admin
POST /survey/{surveyId}/change_code
Rename a question/component code throughout the design.
- Query:
from,to - Auth: super_admin, survey_admin
POST /survey/{surveyId}/design/publish
Publish a survey design version.
- Query:
version,sub_version - Auth: super_admin, survey_admin
POST /survey/{surveyId}/offline/design
Get design differences for offline sync (published version + new resources since the client's last-modified time).
- Body:
{ version, subVersion, lastModified } - Auth: super_admin, survey_admin, surveyor
Survey Execution
Survey run endpoints are public (no authentication required). The parallel preview endpoints require authentication and are used by admins to test surveys.
POST /survey/{surveyId}/run/start
Start a survey session.
- Body:
{ lang?, navigationMode?, values?, clientUTCTime? } - Auth: none
POST /survey/{surveyId}/run/navigate
Navigate to the next/previous page or submit.
- Body:
{ responseId, navigationDirection, values?, events?, clientUTCTime? } - Auth: none
GET /survey/{surveyId}/run/runtime.js
Get the generated JavaScript state machine for client-side execution.
- Auth: none
POST /survey/{surveyId}/preview/start
Start a survey in preview mode.
- Query:
mode(optional:online|offline) - Body:
{ lang?, navigationMode?, values?, clientUTCTime? } - Auth: authenticated
POST /survey/{surveyId}/preview/navigate
Navigate in preview mode.
- Query:
mode(optional) - Body:
{ responseId, navigationDirection, values?, events?, clientUTCTime? } - Auth: authenticated
GET /survey/{surveyId}/preview/runtime.js
Get the runtime JavaScript for preview mode.
- Auth: authenticated
Responses
GET /response/{responseId}
Get a single response with resolved, labelled values.
- Auth: super_admin, survey_admin, analyst
GET /response_with_event/{responseId}
Get a response together with its timeline events.
- Auth: super_admin, survey_admin, analyst
GET /survey/{surveyId}/response/summary
Get a paginated response summary for a survey.
- Query:
page,per_page,status(ALL|COMPLETE|INCOMPLETE|PREVIEW),surveyor,confirm_files_export - Auth: super_admin, survey_admin, analyst
GET /survey/{surveyId}/response/analytics
Get aggregated analytics for survey responses.
- Query:
max_responses(optional) - Auth: super_admin, survey_admin, analyst
GET /survey/{surveyId}/response/export/{format}/{from}/{to}
Export responses in a given format.
- Path:
format(CSV|EXCEL|JSON),from/to(response indices) - Query:
complete,db_values - Auth: super_admin, survey_admin, analyst
GET /survey/{surveyId}/response/files/download/{from}/{to}
Download response files as a ZIP archive.
- Query:
complete - Auth: super_admin, survey_admin, analyst
DELETE /survey/{surveyId}/response/{responseId}
Delete a response.
- Auth: super_admin, survey_admin
Response Files
POST /survey/{surveyId}/response/attach/{responseId}/{questionId}
Upload a file attachment to a response.
- Body: multipart file
- Auth: none
GET /survey/{surveyId}/response/attach/{responseId}/{questionId}
Download a response file attachment.
- Auth: none
GET /survey/{surveyId}/response/{responseId}/attach/{filename}
Download a response file by filename (legacy path).
- Auth: none
POST /survey/{surveyId}/response/preview/attach/{responseId}/{questionId}
Upload a preview/test response file.
- Body: multipart file
- Auth: authenticated
Offline Sync
POST /survey/{surveyId}/response/{responseId}/upload
Upload a complete offline survey response.
- Body:
{ versionId, lang, values?, startDate, submitDate, userId, navigationIndex, events? } - Auth: super_admin, survey_admin, surveyor
POST /survey/{surveyId}/offline/response/{responseId}/upload/{fileName}
Upload a file from an offline response.
- Body: multipart file
- Auth: super_admin, survey_admin, surveyor
POST /survey/{surveyId}/offline/response/{responseId}/upload/{filename}/exists
Check whether an offline response file has already been uploaded.
- Auth: super_admin, survey_admin, surveyor
Survey Resources
POST /survey/{surveyId}/resource
Upload a resource file (images, media) for a survey.
- Body: multipart file
- Auth: super_admin, survey_admin
GET /survey/{surveyId}/resource/{fileName}
Download a survey resource file (cached).
- Auth: none
DELETE /survey/{surveyId}/resource/{fileName}
Delete a survey resource file.
- Auth: super_admin, survey_admin
Autocomplete
GET /survey/{surveyId}/autocomplete/{filename}
Search autocomplete values (respondent-facing).
- Query:
q(search term),limit(1–100, default 10) - Auth: none
GET /autocomplete/{surveyId}/{componentId}
Get all autocomplete values for a question (design-time).
- Auth: super_admin, survey_admin
POST /autocomplete/{surveyId}/{componentId}
Upload autocomplete data for a question.
- Body: multipart file
- Auth: super_admin, survey_admin
Notes
- Dates are UTC wall-clock strings in
yyyy-MM-dd HH:mm:ssformat. - Survey ID in the path determines the survey context; the backend is single-tenant, so no tenant header is needed.
- File uploads go through Multer with a per-request size limit.
- Errors are shaped by a global exception filter; malformed or invalid bodies are rejected by a global validation pipe.