Browser, React, embedded, and hosted
Render an assessment using a token scoped to one participant session—never an app or environment credential.Controller
Pinned consent, next-question or question-page state,
answer/unknown/decline, pause/resume, completion, and callbacks.
Embedded renderer
Dependency-free UI mounted in your DOM with your controller and surrounding
brand.
Hosted renderer
A hardened iframe handoff using the server-returned hosted URL and a
fragment token.
React adapter
An external-store hook that keeps rendering separate from the transport
lifecycle.
Embedded journey
Question pages
A custom UI can show up to five questions at once. SetpageSize (1 to 5) and
the controller loads server-reserved pages from
GET /v1/participant/next-batch instead of one question at a time.
snapshot.pageholds the current page. It stays null until consent is accepted.snapshot.nextmirrors its first question, so code that checkssnapshot.next.completekeeps working.submitPagesends the page’s responses in page order, usually in one request with the page’squestion_viewedevents, then loads the next page.batchSize(events per request, default 20) still applies: a page queues up to twicepageSizeevents, so a smallerbatchSize, or older events queued ahead of the page, can split it across requests.snapshot.submittedPageis the page’sbatch_idfrom the moment its responses are queued until the next page loads or Synapse rejects the page. Show the page read-only whilesubmittedPage === page.batch_id. It is part of the controller state, so it survives UI remounts.- Submitting the same page again with the same answers only retries delivery.
Different answers reject with an
Errorand nothing is queued. - Offline responses stay queued, and the controller loads the next page once the queue is delivered.
- If the answers were delivered but the next page failed to load,
submitPagestill resolves with the same page.snapshot.errorsays why, and the controller retries the load; a “Try again” button can callloadNextPage(). - If Synapse rejects page events (409, 422, or nothing accepted), the
controller drops every page event from the queue, reloads the current page,
sets
snapshot.errorto aParticipantPageRejectedError, and callsonError. This also happens during background delivery and for a queue restored after a reload, so show the “page was refreshed” notice wheneversnapshot.erroris aParticipantPageRejectedError.submitPagerejects with it when its own delivery was rejected. - 401, 403, 404, or 410 mean the link no longer works. The queue is kept, the
page is not reloaded,
snapshot.statusbecomes"error", andsubmitPagerejects with the originalApiError. - Pages served by this server version never hold a question that another page question’s response could hide or show, so raw API clients can post a page’s responses in any order.
- The embedded renderer always shows one question at a time.
Hosted journey
Consent and queue safety
- The controller sends the server-pinned
consent_acceptedevent before any restored or new participant traffic. mountEmbeddedParticipantstarts the controller. Do not callstart()a second time after mounting.- Use
MemoryParticipantQueueStorageby default. Local storage can retain assessment content in clear text and needs an explicit privacy review. - Use an opaque evaluation reference as the queue key—never the participant token, an external ID, or a report token.
skiprecords unknown;declinerecords an explicit refusal. Neither is imputed as a negative answer.- Call
dispose()and destroy the renderer when the host view unmounts.