npm
npm install --save-exact @sightspool/sdk@0.5.0
import Sightspool from '@sightspool/sdk'
Sightspool.init({ key: 'YOUR_GO_LIVE_WIDGET_UUID', audience: 'all_visitors' })
// Optional when a visitor signs in:
Sightspool.identify(currentUser.id)
// On logout (anonymous visitors remain eligible in all_visitors mode):
Sightspool.identify(null)
// When leaving the pages included in your research:
Sightspool.destroy()
Choose an explicit audience. New integrations should use automatic and synchronize auth state as described above. Legacy modes remain available:
all_visitors: anonymous website visitors and signed-in users. Initialize once on the relevant website pages. No identity or login is required; logout continues recruitment as a visitor.signed_in: only users with a real signed-in session. Initialize in your authenticated app shell and callidentify(actualUserId)after authentication resolves. Until then, no request or invitation is made.identify(null)immediately removes invitations on logout.
There is no default audience. Missing or invalid audience configuration stays idle.
identify sends the id you pass, over TLS, to your own workspace's Sightspool
endpoint, on the offer request only. Sightspool hashes it workspace-scoped on
arrival and never stores it raw in any table, log or payload. The SDK holds it in
memory for the page's lifetime and never writes it to storage, a cookie, a URL, a
fragment or a log. Through 0.4.2 the id was never transmitted; from 0.5.0 it
is — it is what lets a cohort-targeted card tell whether you are one of the
people its research question is about. Do not invent IDs for visitors. This client
setting controls display; it does not authenticate a participant or change the
server-approved research cohort.
An id that is not a non-empty string of at most 200 characters after trimming is
treated as unidentified, exactly like identify(null). Over-length ids are never
truncated: a truncated id is a different person.
Matching your analytics identity
Cohort-targeted cards match against journey cohorts derived from PostHog. Your
product must call posthog.identify() with the same id it passes Sightspool.
If you identify PostHog with an email and Sightspool with an internal UUID, those
are different identities that never meet — the match rate is a flat zero that
looks exactly like an empty cohort rather than like a misconfiguration.
Script tag
For visitors, no authentication integration is needed:
<script async src="https://www.sightspool.com/sdk.global.js"
data-sightspool-key="YOUR_GO_LIVE_WIDGET_UUID"
data-sightspool-audience="all_visitors"></script>
For signed-in-only research, listen for readiness before loading the SDK, then synchronize your actual session:
<script>
let sightspoolUserId = null;
function syncSightspoolUser(userId) {
sightspoolUserId = userId;
window.Sightspool?.identify(userId);
}
window.addEventListener('sightspool:ready', () => {
window.Sightspool.identify(sightspoolUserId);
});
// Call syncSightspoolUser(actualUser.id) when authentication resolves.
// Call syncSightspoolUser(null) on logout.
</script>
<script async src="https://www.sightspool.com/sdk.global.js"
data-sightspool-key="YOUR_GO_LIVE_WIDGET_UUID"
data-sightspool-audience="signed_in"></script>
Go live supplies the correct key and endpoint. Local keys belong to the local
Sightspool database and cannot be paired with production. Script installs default
to the origin serving the bundle; data-sightspool-endpoint overrides that for a
CDN or self-hosted install. npm defaults to https://www.sightspool.com.
API
| Method | Behavior |
|---|---|
init({ key, audience, endpoint? }) | Start research for the explicit audience; UUID key required. Repeating the same configuration is idempotent. Changing it tears down the previous runtime. |
identify(userId) | Enable signed-in eligibility, and send that id on the offer request so a cohort-targeted card can match it. Held in memory only, never stored. null clears it — as does any value that is not a non-empty string of at most 200 characters after trimming. Signed-in-only invitations disappear; all-visitors recruitment continues. |
pause() | Pause recruitment, abort the current request and remove the launcher. |
resume() | Resume recruitment for the configured audience. Does not bypass approval or hours. |
destroy() | Remove the launcher, listeners and polling. An already opened interview is not silently ended. |
getStatus() | not_initialized, signed_out, paused, checking, unavailable, available or error. |
pause and resume are recruitment controls, not consent to record. Participants
still review the offer, eligibility and recording consent in Sightspool. Owners
approve the research setup separately. The backend remains authoritative for
availability, capacity, signed offers and interview admission.
React / Next.js
Use a client component on the pages included in your research. Identity is optional for all visitors:
'use client'
import { useEffect } from 'react'
import Sightspool from '@sightspool/sdk'
export function Research({ userId = null }: { userId?: string | null }) {
useEffect(() => {
Sightspool.init({ key: 'YOUR_GO_LIVE_WIDGET_UUID', audience: 'all_visitors' })
Sightspool.identify(userId)
return () => Sightspool.destroy()
}, [userId])
return null
}
Optional @sightspool/react 0.2.0 provides SightspoolProvider and research hooks.
It requires SDK 0.4.x; see packages/react/README.md. Use one instance per document.
Privacy and verification
The offer request contains the public workspace key, a random per-workspace
browser-session device token, the operation name, and a real user ID when supplied.
Automatic mode also sends installation: "automatic-v1". No traits, auth
secrets, DOM text or page URL are included. Requests use credentials: 'omit'.
Offer requests explicitly suppress the Referer header; Origin and ordinary network metadata still reach the service.
No microphone or recording starts on initialization or launcher display.
Polling occurs at most once per 15-second interval while visible and eligible for the configured audience, plus explicit identity/resume/visibility changes. Only one request is in flight; requests time out after 10 seconds. Identity changes, pause, hidden tabs and destruction invalidate stale responses. Logout removes signed-in-only invitations; all-visitors mode refreshes as a visitor.
A connection receipt proves that the SDK contacted the configured server. It does not certify your app's complete login/logout flow or a successful interview. Test anonymous visits, login, navigation, logout, slow loading and unmounting in both modes. Use Go live for server connection checks. A closed User Hours window can connect without an invitation. Do not fabricate approval or weaken an origin/CSP policy to make it show.
Verified script releases
Go live generates a versioned, content-addressed script URL with SHA-384 integrity,
crossorigin="anonymous" and referrerpolicy="no-referrer". Use those values together.
The unversioned script examples above are a convenience URL whose contents can change.
To pin a reviewed release, use Go live or the manifest linked from the data and security guide.
For self-hosting, copy the reviewed bundle and preserve its integrity value. Set
data-sightspool-endpoint to the workspace Sightspool origin explicitly, because
script auto-init otherwise defaults to the bundle host. Keep your own CSP and nonce
requirements; allow only the required script host and Sightspool connection origin.