@insightdive/sdk
v0.9.0
Published
JavaScript/TypeScript SDK for Insightdive in-app surveys. Embeds a survey as a modal bottom sheet or inline widget, streams lifecycle events.
Maintainers
Readme
@insightdive/sdk
JavaScript / TypeScript SDK for Insightdive — the in-app feedback platform.
Embeds a survey as a modal bottom sheet or inline widget, streams lifecycle events back to your app. Anonymous by default — no user identifiers are ever collected or passed to the server.
Install
npm install @insightdive/sdk
# or
yarn add @insightdive/sdk
# or
pnpm add @insightdive/sdk3-line integration
import { Insightdive } from '@insightdive/sdk';
Insightdive.configure({
tenant: 'acme', // your workspace slug
survey: 'onboarding', // the project slug
apiKey: 'ik_abc123...', // copy from Admin → Settings → API
});
// anywhere later:
const result = await Insightdive.show();
if (result.status === 'completed') {
console.log('Submitted! submissionId:', result.submissionId);
}Configuration
| Field | Type | Description |
|---------------------|--------------------------------------------------|-------------|
| tenant | string | Workspace slug — the subdomain on insightdive.com (e.g. 'acme'). |
| survey | string | Project slug from the admin (e.g. 'onboarding'). Always serves the currently active deployment. |
| apiKey | string | Tenant API key found in Admin → Settings → API. Required. |
| baseUrl | string? | Full URL override for staging or self-hosted instances (e.g. 'http://acme.localhost:3000'). |
| productVersion | string? | Free-form version stamped on every submission (e.g. '1.2.3'). |
| productIdentifier | string? | Surface slug — useful when one app embeds the SDK in several places. |
| locale | string? | Locale to render the survey in. Falls back to the survey's default. |
| theme | string? | 'light' or 'dark'. Falls back to the survey's authored theme. |
| scale | number? | Content zoom factor (CSS zoom), clamped to [0.5, 2]. Default 1. Shrinks/enlarges the survey content to fit the modal/iframe instead of being clipped. |
| context | Record<string, string \| number \| boolean>? | Operator-defined key/value metadata stamped on every submission. See Operator context. |
| respondentToken | string? | Opaque hash for operator-side cross-referencing without storing a user identity. See Respondent token. |
Check availability first
const available = await Insightdive.isAvailable();
if (available) {
await Insightdive.show();
}isAvailable() hits /api/v1/surveys/{survey}/status with a 5-second timeout and returns false on any error, so it's safe to call on every page load.
Modal sheet
const result = await Insightdive.show();
// Resolves when the sheet closes (completed or dismissed)
console.log(result.status); // 'completed' | 'dismissed'
console.log(result.sessionId);
console.log(result.submissionId); // only present when status === 'completed'
console.log(result.duration); // millisecondsDismiss the sheet programmatically:
Insightdive.hide();Inline widget
Render the survey directly inside any container element — useful for dedicated feedback pages or embedded panels:
const container = document.getElementById('survey-slot');
// container needs an explicit height in CSS
Insightdive.embed(container);<div id="survey-slot" style="height: 600px; width: 100%;"></div>Lifecycle events
Insightdive.on('ready', () => {
console.log('Bridge alive');
});
Insightdive.on('viewed', (e) => {
console.log('Session registered', e.sessionId);
});
Insightdive.on('started', () => {
console.log('User clicked start');
});
Insightdive.on('completed', (e) => {
analytics.track('feedback_completed', { id: e.submissionId });
});
Insightdive.on('dismissed', () => {
analytics.track('feedback_dismissed');
});
// Catch-all
Insightdive.on('*', (e) => {
console.log(e.type, e);
});Unsubscribe:
const handler = (e) => console.log(e);
Insightdive.on('completed', handler);
Insightdive.off('completed', handler);Event-based triggering
trigger() calls the status endpoint with ?event=<name> before opening the modal. The survey only opens when the insight's trigger-event list (configured in the admin) includes the event name. When the list is empty every event matches.
// Fires the survey only when 'app_launched' is in the insight's trigger list.
await Insightdive.trigger('app_launched');On network error the SDK falls through and opens the survey anyway (fail-open).
Frequency capping (cooldown)
isAvailable(), checkAvailability(), and trigger() all respect the cooldown configured on the insight (Admin → Insight → General → Cooldown). The last shown timestamp is stored in localStorage per tenant+survey pair. If the cooldown period hasn't elapsed the call resolves without showing the modal.
Operator context fields
Stamp arbitrary application context on every submission — plan tier, feature flags, active surface, A/B cohort — without collecting any user identity. The admin can filter, segment, and run AI analysis per context dimension.
Insightdive.configure({
tenant: 'acme',
survey: 'onboarding',
apiKey: 'ik_abc123...',
context: {
plan: 'enterprise',
trialDaysRemaining: 12,
featureExportV2: true,
surface: 'settings',
} satisfies Record<string, string | number | boolean>,
});Constraints (enforced server-side; invalid entries are silently dropped):
- Max 20 keys per object
- Key format:
[a-z0-9_], max 64 characters - Values:
string,number, orboolean— no nested objects - String values: max 255 characters
- Total JSON payload: max 4 KB
Context values are visible in Admin → Responses (detail view) and included in CSV exports as context.<key> columns.
Respondent token
Let the operator correlate responses with their own user records — without ever exposing a user identity to Insightdive. Compute an opaque hash client-side and pass it as respondentToken:
async function computeToken(userId: string, workspaceSalt: string): Promise<string> {
const data = new TextEncoder().encode(userId + workspaceSalt);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
Insightdive.configure({
tenant: 'acme',
survey: 'onboarding',
apiKey: 'ik_abc123...',
respondentToken: await computeToken(currentUser.id, 'my-secret-salt'),
});What Insightdive stores: the hash only — no ability to reverse it to a user.
What the operator can do: compute the same hash from their own database and look up all responses for that hash in Admin → Responses.
GDPR note: Insightdive is a processor with no identification capability. The operator (controller) owns the correlation.
Tip: rotate
workspaceSaltif you want to "forget" historical correlations without deleting data.
Constraints: max 128 characters, characters [a-zA-Z0-9-_] only.
Screenshot capture
When an insight has screenshot collection enabled (Admin → Insight → Settings → Delivery), the SDK captures the current page with html2canvas right before the modal opens and attaches it to the response. The screenshot is of the page UI — never the user.
- No setup required:
html2canvasis loaded lazily (dynamicimport()), so it only enters the bundle graph when a screenshot is actually captured. - The flag is read from the status endpoint, so call
isAvailable()/checkAvailability()/trigger()beforeshow()for the SDK to know capture is on. - Capture is best-effort: any failure is swallowed and the survey still opens.
- The image is downscaled to ≤1280px wide and encoded as JPEG to keep the payload small.
// Capture happens automatically inside show() when the insight opted in.
if (await Insightdive.isAvailable()) {
await Insightdive.show();
}Advanced availability check
checkAvailability() returns the full status payload for diagnostics:
const { available, reason, error } = await Insightdive.checkAvailability();
// reason: 'disabled' | 'not_published' | 'not_found' | 'cooldown'
// error: set on network failure
if (!available) console.warn('Survey unavailable:', reason ?? error);TypeScript
All types are exported:
import type {
InsightdiveOptions,
FeedbackResult,
FeedbackEvent,
FeedbackCompletedEvent,
} from '@insightdive/sdk';Platform support
Works in all modern browsers (Chrome, Firefox, Safari, Edge). Requires a DOM environment — not suitable for Node.js server-side rendering without conditional guards.
Privacy
The SDK does not collect or transmit any user-identifying data by default. Only deploymentId, optional productVersion, optional productIdentifier, locale, and theme are forwarded to the server. Submissions are linked to a SurveySession via an opaque id that never leaves the device tied to anything personal. Insightdive cannot identify any respondent — anonymous to us by architecture.
The optional context map contains only operator-stamped metadata — no PII.
The optional respondentToken is an opaque hash the operator computes themselves; Insightdive never decodes it.
License
MIT. See LICENSE.
