Electron Vite SDK
Measure desktop app activity from the Electron main process.
For an established app, read Add Jelto to an app with existing users before interpreting install counts, update history or cohorts.
Set up with AI
For a copyable setup prompt with your app details, open Settings → Installation → Apps, expand your app's SDK setup, choose Electron and select Copy prompt. The prompt names the package described below. See Set up with AI for what to expect.
Setup steps
- Install
@jelto/electronas a runtime dependency. - Add initialization to the Electron Vite main-process entry.
- Send declared actions from validated main-process handlers.
- Verify both development and packaged builds.
Install
Node.js 18 or later is required. The package is published on npm as @jelto/electron; its source and releases are at usejelto/electron-sdk. Install it in your app project:
npm install @jelto/electron
Include it in your packaged app's runtime dependencies, not only in development dependencies.
Initialize in the main process
Add the initialization to src/main/index.ts, or the main entry configured by your Electron Vite project. Keep your existing window-creation code. The analyticsAllowed argument below comes from your app's saved telemetry choice.
import { app } from 'electron'
import jelto from '@jelto/electron'
async function startAnalytics(analyticsAllowed: boolean) {
await app.whenReady()
if (analyticsAllowed) jelto.init('YOUR_PRODUCT_ID', undefined, undefined, 'new')
}
Call startAnalytics from your app's startup flow. Replace YOUR_PRODUCT_ID with your product ID (prd_8f3kq2m9x1). Register the app in Settings → Installation → Apps for every operating system you release. Without an app argument, each build reports under its own operating system's app (macos, windows or linux), so one initialization serves all of them. If you registered a custom identifier, pass it as the second argument.
The third argument is the optional endpoint; undefined keeps the SDK default. Pass 'new' as the fourth argument for an app that had no users before Jelto; otherwise see Add Jelto to an app with existing users. Without it, installations are recorded as unknown, which Retention, Onboarding and license conversion exclude.
In an existing, validated main-process action handler, report:
jelto.track('export_finished', { format: 'pdf' })
Renderer actions
Keep the SDK in the main process. If an action starts in the renderer, expose a narrow method through your existing preload bridge and IPC handler. For example, forward an export-completed action with an allowlisted format. Do not expose arbitrary event names, arbitrary payload forwarding or SDK initialization to remote renderer content.
New and existing installations
If your app had users before Jelto, derive the classification from each installation's saved state instead of hardcoding one value, and read that state before your app changes it. For an installation that already existed before Jelto:
jelto.init('YOUR_PRODUCT_ID', undefined, undefined, 'existing')
Use new only when the host knows this is the app's first launch; use existing
for a saved earlier installation and unknown when uncertain (the default).
Pass the lowercase string as the fourth argument, after the optional app and endpoint.
Never send a first-launch date.
The first claim freezes the classification across retries and later launches.
Older claims stay unknown and are excluded from new-install cohorts. See
Add Jelto to an app with existing users for rollout,
coverage and cohort rules.
Onboarding and custom events
The onboarding call sends onboarding:<step> with a status and optional reason:
jelto.onboarding('permissions', 'ok')
jelto.onboarding('permissions', 'fail', 'denied')
The step must match ^[a-z0-9_-]{1,32}$: 1–32 lowercase ASCII letters, digits, underscores or hyphens. Status must be ok, fail or skip. A non-empty reason must match ^[a-z0-9_.-]+$ and be at most 64 characters; an omitted or empty reason sends no reason property. If any of these values falls outside the grammar or length cap, the whole event is dropped. The rejection is visible only in debug logging, not in the dashboard.
Enable local debug logging with JELTO_DEBUG=1.
Steps feed onboarding_reached, onboarding_ok, onboarding_fail and onboarding_skip, broken down by onboarding_step; status reports use each install's first result for that step. They also feed onboarding_reason, which groups failures by onboarding_reason and requires an onboarding_step filter. onboarding_cohort supplies the explicitly new install population (existing and unknown origins are excluded), and onboarding_completed uses the final step's ok result.
Every valid jelto.track() event received by Jelto becomes goal:<name> on the app surface, with per-install conversion and prop:<key> breakdowns. Custom events and their property keys are discovered on first receipt; no event or goal registration is required. For the export example, query goal:export_finished with surface=app and dimension=prop:format.
The source of truth for these SDK event contracts is spec/wire-v1.md §4; §7 of the same document defines custom event discovery.
License properties
After initialization, send the current license from your app's saved entitlement state, including for existing customers. Send it again when the state changes:
jelto.setProps({ license: 'paid' })
Accepted license values are strings matching ^[a-z0-9_.-]{1,24}$, such as free, trial, paid or expired; these examples are not a fixed enum. Uppercase letters, spaces and values longer than 24 characters are rejected. license_share breaks the live install fleet down by the stored license value. license_conversion counts an install as converted only when its latest stored license equals the product's paid_license_value setting by string equality; the setting defaults to paid. If no stored values ever match, an otherwise reportable cohort stays at a true, permanent 0 % even though the query succeeds; align the value in the product settings or through the account API's paid_license_value field.
Verify
Launch the app after enabling telemetry in your own app. Trigger an export, then inspect the product's app activity and Goals for the current date. Jelto discovers export_finished and its format property when it receives the event; no event registration is required.
The SDK sends its first batch, the install claim and a heartbeat, about 2 seconds after initialization, and later events about 5 seconds after they are tracked; if the device is offline or Jelto asks it to retry later, it retries with backoff. Jelto stores events within about a second, so Settings → Installation → Apps shows Receiving app activity a few seconds later; choose Check app activity to refresh. Release versions support version-adoption reporting; later version changes are reported without creating a new install. Retention requires elapsed time and a mature sample.
If data is missing, check the product ID, registered app slug, collection permission and network access. Debug payload logging is for local diagnosis only; turn it off before distributing a build. Do not reset the install ID on each launch. See Understand app usage.
Collection lifecycle
jelto.setProps({ license: 'paid' }) updates install properties. jelto.disable() wipes analytics state and stops subsequent collection until another authorized initialization. jelto.reset() rotates the install identity. Keep each running app process's analytics storage separate.
Test both a development build and the packaged app: packaging can omit a runtime dependency even when development imports work.
Track updater outcomes
Use the existing tracking API to send app_update stages, failures, and explicit postponements. They appear under App updates → Update activity, separately from version changes confirmed on launch. See Track update activity for properties and updater callbacks.