Embed a survey
Run one question or a complete questionnaire inside another survey. Choose the online connector or the separate offline SurveyToGo package below.
Choose online or offline first
The online packages are connectors to a published survey. The offline SurveyToGo package contains the questionnaire itself. Choose one workflow before downloading; the packages are not interchangeable.
| Workflow | Connection during collection | Where answers are saved |
|---|---|---|
| SurveyToGo · Online | Required | voice + SurveyToGo |
| SurveyCTO · Online | Required | voice + SurveyCTO |
| Qualtrics · Online | Required | voice + Qualtrics |
| SurveyToGo · Offline | Not required | SurveyToGo only |
voice currently supplies no offline SurveyCTO or Qualtrics adapter. Their own offline features do not change this. Offline SurveyToGo has no automatic upload into voice and no voice results or analysis.
All platform packages still require acceptance testing in your actual account or devices. The runnable examples demonstrate the integration, not vendor certification.
How answers reach the host
The host is the outer survey, such as SurveyToGo, SurveyCTO or Qualtrics. The installed voice adapter writes the module’s recorded answers into that host interview. You do not need to download voice results and import them into the host after every interview.
- Online: voice prepares the recorded answer and saves a retry checkpoint with the host. It sends the answer to the voice server. After server acceptance, the iframe passes the recorded report through the bridge’s MessageChannel to onReport(). The host adapter saves it.
- When the online module finishes, onComplete(result) delivers all its recorded answers together. The host must save that result before acknowledging completion. This happens inside the running host survey; it is not a later server-to-server export.
- Offline SurveyToGo: the local voice runtime writes recorded answers directly into the current SurveyToGo question through its package adapter. No voice server or online bridge is involved. The host handles later synchronization and export.
| Host | Destination in the host interview |
|---|---|
| SurveyCTO | Text field strudl_result; filled at module completion.JSON.parse(value).reports |
| Qualtrics | Text Entry answer strudl_result; submitted when the host page advances.JSON.parse(value).reports |
| SurveyToGo | The custom module question’s JSON answer; updated during collection, online or offline.JSON.parse(value).result.reports |
Here value means the exported answer text from that host field. Several voice questions fit in this one JSON field; they do not automatically become separate host columns. The platform guides show the exact destination, a sample result and code to extract individual recorded answers.
Result data and resume state serve different purposes. The result contains only recorded answers and protocol identifiers/status. Resume state lets the same interview continue and may contain credentials. SurveyToGo stores both inside one answer, so extract its result before sharing data with analysts.
Set up an online integration
These instructions load the survey from voice over the internet. Saving a ZIP on a tablet does not make this integration offline. If the connection drops, collection pauses; reconnect and resume the same interview.
A host is the outer survey that opens voice and receives its recorded results. For this setup you need permission to edit that host survey, a published voice survey, and its integration code.
- Publish a survey with one or more questions and a closing date that covers your fieldwork.
- On its management page, open Embed in another survey. Choose a label and consent mode, then generate an integration code. Save the code when it is shown; you can revoke it in the same panel.
- Copy the integration snippet or download the generic test host. Use the dedicated iframe URL and integration code. Replace the example storage callbacks with durable storage for each interview in your host platform.
Codes allow participation in this survey, not management. Keep codes and interview credentials out of URLs. Use HTTPS, except for local tests. Third-party cookies are not required.
Serve the generic example on a different origin from the survey site. Use synthetic interviews to check saving, reloads, lost connections and completion before fieldwork.
Try the generic online host on your computer
Download and extract the generic ZIP. Keep index.html, host.js, host.css and embed-bridge.js together. Open a terminal in that folder and start the local server below; then open http://127.0.0.1:8081/ in the same computer’s browser.
Run these commands in a terminal on your computer, from the extracted package folder. Replace the example folder path with your own. On Windows, use py -3 instead of python3 if needed. Keep the terminal open while testing.
cd "/path/to/extracted/package"
python3 -m http.server 8081 --bind 127.0.0.1
This local server is only for testing on the same computer. A localhost or 127.0.0.1 address on a tablet points to the tablet itself. Fieldwork uses the installed host package or the published host survey, not this local test address.
generic-host/
├── index.html
├── host.js
├── host.css
├── embed-bridge.js
├── example-questionnaire.md
└── README.md
Fill in the dedicated iframe URL, integration code and synthetic interview reference in the test page. Leave the consent version blank for respondent consent. Click Open / resume interview. Complete the module, reload the page, and reopen with the same reference: the recorded answers should reappear without a new submission.
This generic page is a browser-storage demonstration. For fieldwork, use a platform guide above or implement the durable host callbacks below. Do not use the local test page as a shared production survey.
Host inputs
Load embed-bridge.js and call StrudlEmbed.mount(options). Supply iframe, url, code, the interview's savedState (or null initially), and the save callbacks below. Optional externalReference is an opaque interview reference, not authentication.
Pass enumerator IDs, respondent IDs or routing context through hostData:
hostData: {
enumerator_id: "E007",
respondent_id: "R0042",
followup: true
}
In Variables and treatments, bind each field you want to use:
enumerator_id = host("enumerator_id", "")
respondent_id = host("respondent_id", "")
followup = host("followup", False)
Use $respondent_id in question text or followup in routing. The second argument is the fallback when that field is absent, including standalone links and previews. Values must match the default's type; integer and decimal defaults both accept numbers. A None default allows any supported scalar.
Host data accepts up to 50 identifier keys and 16 KiB in total: strings up to 2,000 characters, finite numbers, booleans or null. Keep IDs as strings to preserve leading zeros. Inputs are encrypted and fixed at first start; reopening keeps the original inputs and treatment assignment.
The configured SurveyToGo Android package currently uses respondent consent and no native host-data mapping. Keep native identifiers beside its module answer for later extraction. The options above describe the generic bridge and adapters that explicitly support them.
Recorded results
For a custom host, the bridge delivers JavaScript objects to the callbacks below; it does not know how to write your platform’s database. Implement those save callbacks, or use a supplied platform adapter. onReport receives one accepted answer; onComplete receives the whole module result.
onState(state)- Required: save the exact resume state for this interview before resolving. Collection waits for that acknowledgement. Reject if saving fails. Keep state separate from analyst results: it contains a resume credential and possibly a pending reported answer.
onReport(report)- Save a server-accepted answer. Deduplicate by
event_id: recovery may deliver it again. Protected questions return their garbled report, ordinary questions their recorded value, and skips an explicit marker. onComplete(result)- Save the final result before resolving and allowing the host to advance. Completion requires server completion and host acknowledgement. The result contains all recorded answers, not just the last question.
onError(error)onResize(height)- Display integration or storage errors and handle size changes. The bridge also adjusts the iframe height. Destroy the bridge before replacing an interview.
{
"version": 1,
"survey": "survey-uuid",
"run_id": "run-uuid",
"completed": true,
"reports": [
{"question": "q1", "reported": 0, "event_id": "run-uuid:q1"}
]
}
Reports use reported for a zero-based choice index, text, number, multiple for a list of indices, or skipped: true. Explanation cards produce no answer report. Results include protocol identifiers and completion status, never intended protected answers, garbling draws, internal variables or a copy of host inputs.
Authorized exports here include host context under integration_linkage in JSON and in integration_linkage.csv in the analysis bundle. These use export pseudonyms and are separate from the results returned to the host.
In the generic test host, host.js saves state, reports and result in this browser’s localStorage under the voice URL and interview reference. The displayed reports are a demonstration; they are not saved to SurveyToGo, SurveyCTO or Qualtrics. Use a platform adapter or replace save() with your host’s durable interview storage.
Consent and withdrawal
The default keeps the respondent consent screen. External consent requires an exact versioned script and confirmation for each interview. It records the host attestation, not a claim that the respondent clicked consent boxes or accepted the Terms here.
consent: { confirmed: true, script_version: "study-v1" }
Set that confirmation from this interview’s actual consent field; never hard-code consent for every participant. The script must cover voluntary participation, processing, sharing recorded answers with the host, and withdrawal.
Participants can leave and retain access to privacy information and question-protection explanations. The private withdrawal link stays inside the iframe. Save it when starting; reopening does not create a replacement. Withdrawal deletes this service’s copy and linkage, but does not automatically delete host-held or exported copies.
Recovery and fallbacks
- Interrupted connection
- Implemented: pause and retry, or reopen using the saved interview state. Pending randomized reports are reused and server receipts are reconciled. Do not clear state or create a replacement interview to retry.
- Missing host data
- Implemented:
host("field", default)supplies the default when the field is missing. Invalid supplied values are rejected; they are not silently replaced. - Unsupported iframe
- No automatic separate-window or redirect fallback is implemented. A normal public survey link starts a separate standalone submission and does not return results to the host. It cannot resume an embedded interview.
- Offline collection
- The online bridge requires a connection. For planned offline SurveyToGo fieldwork, export the separate offline package from the editor. It stores answers in the host only and cannot resume an online submission. Setup and limitations
If something does not work
- The module does not open
- Check that you replaced both placeholders, used the /embed/s/ URL from Embed, and supplied an active integration code. Check the internet connection and the host’s iframe or JavaScript policy. A public survey link is not an integration URL.
- Consent is rejected
- Match the consent mode and script version exactly. For external consent, confirm the host field is 1 for this interview. Reopen after correcting the host mapping; do not hard-code consent.
- Next stays disabled or the answer looks empty
- Finish every required voice question and check for a connection or save error. Resume state is not completion. On Qualtrics, continue past the module page and finish the host survey before checking the export.
- The interview will not resume
- Restore the original host interview and its saved state. In Qualtrics, use the same browser profile and origin, and close another tab of that interview. Do not erase state or change the interview ID to repair an error.