LUIDA Docs

Web Console API (POST /api/cluster)

Every dispatch type accepted by the Web Console's callExternal endpoint, with request and response shapes.

These pages are not yet fully reviewed. The LUIDA team is continuing to review and improve them. If you find anything wrong on these pages, or have questions that aren't resolved by reading them, please ask or report to the LUIDA team.

The Web Console exposes a single public endpoint, POST https://luida.cluster.mu/api/cluster, that all of LUIDA's callExternal traffic goes through. Requests are dispatched by a type field. This page lists every supported type.

Common request shape

Every request is a POST with body:

{
  "request": "<JSON-stringified inner object>"
}

The inner object is the per-type payload. It always includes:

  • type — one of the values listed below.
  • token — your experiment's verify token.

Per-type extras follow.

Authentication

Every request must include the verify token registered for the calling experiment. The Web Console rejects mismatched tokens with HTTP 401. Cluster's callExternal endpoint registration also serves as IP-level authentication — calls from outside Cluster's infrastructure are rejected at the network layer.

Dispatch types — quick reference

typeDirectionUsed byOne-line summary
questListWorld → ConsoleRecruitment WorldList accepting experiments for the bulletin board.
questInfoWorld → ConsoleRecruitment WorldFetch one experiment's metadata + consent form.
questionsWorld → ConsoleExperiment WorldFetch questionnaire questions for a qID.
questionAnswersWorld → ConsoleExperiment WorldSubmit a participant's questionnaire answers.
uploadCustomDataWorld → ConsoleExperiment WorldUpload a batch of custom-data records.
checkJoinEligibilityWorld → ConsoleExperiment WorldValidate a participant joining; create or join a session.
saveSessionConditionsWorld → ConsoleExperiment WorldPersist the between-subjects assignment for a session.
updatePlayersCount(stub)ReservedNo-op; reserved for future use.

questList

List experiments currently accepting participants. Used by the Recruitment World to populate its bulletin board.

Request:

{
  "type": "questList",
  "page": 0,
  "number": 20,
  "isTest": false
}

Fields:

  • page (integer) — 0-based page index for pagination.
  • number (integer) — page size.
  • isTest (boolean) — if true, returns only experiments in TESTING status; otherwise returns PUBLISHED.

Response:

{
  "quests": [
    {
      "eID": "<experiment-id>",
      "title": "...",
      "isTest": false,
      "isAccessible": true,
      "allowedPlatforms": ["VR", "WINDOWS", "MACOS"]
    },
    ...
  ],
  "allQuestsCount": 47
}

questInfo

Fetch one experiment's full metadata, including consent form, avatars, and prerequisites.

Request:

{
  "type": "questInfo",
  "id": "<experiment-id>"
}

Response: experiment object with title, description, prerequisite, reward, imageURL, worldID, routingKey, isTest, isAccessible, allowedPlatforms, consentForm{...}.


questions

Fetch questionnaire questions, paginated. Cluster's callExternal payload limit is ~1 KB, so the Console returns a chunk at a time.

Request:

{
  "type": "questions",
  "eID": "<experiment-id>",
  "qID": 1,
  "startIndex": 0
}

Fields:

  • qID (integer) — 1-based questionnaire position.
  • startIndex (integer) — first question index to return; the client paginates by feeding back the next index.

Response:

{
  "questions": [
    { "i": 1, "t": "Age", "d": "...", "r": true, "a": [] },
    ...
  ],
  "isDone": false
}

i = question type integer (1 = Likert, 2 = checkbox, 3 = text, 4 = open-text, etc.); t = title; d = description; r = required; a = answer-options array.

isDone is true when the last chunk has been delivered.


questionAnswers

Submit a participant's answers for one questionnaire.

Request:

{
  "type": "questionAnswers",
  "eID": "<experiment-id>",
  "qID": 1,
  "pID": 1,
  "pRole": "participant",
  "answers": {
    "1": ["32"],
    "2": ["3"]
  }
}

Fields:

  • pID (integer) — 1-based participant index.
  • pRole (string) — usually "participant". Reserved for future role distinctions.
  • answers (object) — keyed by question ID; each value is a string array (single-element for non-multi-select types).

Response: { "success": true, "message": "..." } or 4xx with error message.


uploadCustomData

Upload a batch of custom-data records (and/or participant info records, depending on shape). Called repeatedly by CustomDataUploader.js for paginated uploads.

Request:

{
  "type": "uploadCustomData",
  "eID": "<experiment-id>",
  "pID": 1,
  "sID": "<session-id>",
  "data": {
    "data": [ {...record1}, {...record2}, ... ]
  }
}

The nested data.data shape is intentional — the outer data field can also contain pInfo (participant info) and idfc2userId (identifier mapping) for the participant-info upload variant.

Response: { "success": true, "message": "...", "data": {...} }.


checkJoinEligibility

Validate that a participant is allowed to join a given experiment, and either create a new session or join an existing one. Called by ParticipantManager.js when each participant enters the world.

Request:

{
  "type": "checkJoinEligibility",
  "eID": "<experiment-id>",
  "sID": "<session-id-or-undefined>",
  "envInfo": [
    { "isVr": true, "isWindows": false, "isMacOs": false, "isAndroid": false, "isIos": false }
  ]
}

Fields:

  • sID (string, optional) — if absent, this is the first joiner; the Console will create a new session.
  • envInfo (array, one entry per joining participant) — platform metadata used for allowedPlatforms enforcement.

Response:

{
  "eligible": true,
  "sID": "<session-id>",
  "existingConditions": [
    { "<varName>": "<value>", ... },
    ...
  ]
}

or { "eligible": false, "reason": "<machine-readable string>" }.

existingConditions is the list of past sessions' between-subjects assignments — ConditionManager.js uses it for least-recently-used balancing.


saveSessionConditions

Persist the between-subjects assignment chosen for a session. Called once per session, after checkJoinEligibility succeeds and ConditionManager picks the assignment.

Request:

{
  "type": "saveSessionConditions",
  "eID": "<experiment-id>",
  "sID": "<session-id>",
  "betweenSubjectsConditions": "{\"depth\":\"near\"}"
}

betweenSubjectsConditions is a stringified JSON object — sometimes sent as string, sometimes as object; the Console accepts both.

Response: { "success": true }.


Errors

The Web Console returns 4xx for client errors with a JSON body:

{ "error": "<machine-readable code>", "message": "<human-readable detail>" }

Common error codes:

  • 401 — verify token mismatch or missing.
  • 404 — experiment not found (wrong eID, or experiment deleted).
  • 403 — experiment not accessible (test-mode mismatch, ToS not accepted, session count exceeded).
  • 400 — payload malformed.
  • 500 — server error; retry advisable.

For 4xx, the world should not retry — the failure is deterministic. For 5xx, callers retry with backoff.


See also