Browser geolocation and risk signals

Kade Web SDK

Collect location, device fingerprints, and browser integrity markers, then evaluate geofence and fraud policy through one browser SDK.

~113 KB minified ~38 KB gzipped AES-GCM encrypted requests GPS or IP location flow

Overview

The SDK exposes a global GeoFenceSDK constructor. It encrypts ping and server fingerprint payloads, manages location collection, and supports one-off or continuous checks.

Every ping

Fingerprint data and static browser markers are included on every ping, alongside active markers and location data when available.

Predictable lifecycle

Calling init() during application startup is recommended. Public async methods also initialize automatically when needed.

Browser requirements. Use a modern secure-context browser with the Geolocation, Web Crypto, and IndexedDB APIs. Location permission behavior varies by browser and operating system.

Install

Add the CDN bundle before your application code. The published bundle creates window.GeoFenceSDK.

<script src="https://sdk.videntrix.com/latest.js"></script>

Current production bundle size: ~113 KB minified / ~38 KB gzipped.

TypeScript definitions are separate. Loading the CDN script does not make declarations available to the TypeScript compiler. Download or reference the declaration file as described in TypeScript.

Quick start

Initialize once, request location from a user gesture when your experience needs an explicit permission prompt, then send a ping.

const sdk = new GeoFenceSDK({
  apiToken: 'your-api-token',
  regionId: 123
});

sdk.onError((error, context) => {
  console.error(`Kade SDK error in ${context}`, error);
});

// Recommended at startup; ping() also initializes automatically.
await sdk.init();

// Call from a click or tap handler if permission may need to be prompted.
document.querySelector('#verify-location').addEventListener('click', async () => {
  await sdk.requestLocationPermission();

  const result = await sdk.ping({
    userId: 'player-123',
    source: 'login',
    email: 'player@example.com'
  });

  if (result.blocked) {
    console.log('Blocked:', result.code, result.message);
  } else {
    console.log('Inside:', result.inside);
    console.log('Area:', result.area?.name);
  }
});
Location opt-out is session-sticky. Once a ping or continuous session starts with skipLocation: true, that SDK instance will not request geolocation for the remainder of the session. Create a new instance if your flow changes mode.

Core API

These methods form the documented host-facing surface used by browser integrations.

new GeoFenceSDK(config: GeoFenceConfig) Creates an instance. apiToken is required.
onError(handler: (error: Error, context: string) => void): this Registers an SDK error observer and returns the instance for chaining. Promise rejections should still be handled by the caller.
init(): Promise<void> Prepares active/static markers, fingerprinting, persisted client identity, and encryption. Recommended for predictable startup; async SDK methods auto-initialize if it has not been called.
getFingerprint(userId?: string): Promise<FingerprintResult | FingerprintServerResponse> With no ID, returns the local FingerprintResult. With a userId, sends an encrypted fingerprint request and returns an enriched FingerprintServerResponse.
ping(options?: PingOptions): Promise<PingResponse> Sends one encrypted geofence check. Fingerprint and static markers are included on every ping.
startContinuousPing(options?: PingOptions, callback?: (response: PingResponse, error?: Error) => void): Promise<void> Starts adaptive polling. The SDK uses the server-provided interval for subsequent checks and reuses the supplied options.
stopContinuousPing(): void Stops continuous checks and associated location watchers.
destroy(): void Stops monitoring and clears SDK resources, caches, handlers, and initialization state.
primeLocation(): Promise<GeoLocation> Explicitly prompts when needed and obtains the initial high-accuracy fix. Call from a user gesture.
requestLocationPermission(): Promise<GeoLocation> Alias of primeLocation() for permission-oriented flows.
estimateLocation(options?: { userId?: string }): Promise<LocationEstimateResult> Uses network probes to estimate province and country without triggering a location prompt. It resolves to null fields when no estimate is available and does not throw for internal failures.
evaluateEmail(options: EvaluateEmailOptions): Promise<void> Submits a registration email for operator-side evaluation. It is fire-and-forget: the endpoint returns an empty response and SDK errors are swallowed so registration is not interrupted.

Location permissions

Browsers are most reliable when permission is requested in direct response to a user click or tap.

const allowButton = document.querySelector('#allow-location');

allowButton.addEventListener('click', async () => {
  try {
    const location = await sdk.primeLocation();
    console.log(location.latitude, location.longitude, location.accuracy);
  } catch (error) {
    console.error('Location was unavailable', error);
  }
});

Browser location

Omit skipLocation. The SDK uses its location buffer and watcher after the initial fix. Permission denial is represented in the ping flow and may produce a block response.

IP fallback only

Pass skipLocation: true to avoid browser geolocation. The server can use request-network location signals when available; accuracy and availability are environment dependent.

Network-only estimate

const estimate = await sdk.estimateLocation({ userId: 'player-123' });

if (estimate.province || estimate.country) {
  console.log(estimate.province?.code, estimate.country?.code);
} else {
  // No answer: feature disabled, insufficient beacons, or degraded scoring.
}

Email evaluation

Submit registration identity data for server-side fraud evaluation. The verdict is available to operators through backoffice or webhooks, never returned to the browser.

await sdk.evaluateEmail({
  email: 'new.user@example.com',
  firstName: 'New',
  lastName: 'User',
  userId: 'player-123'
});
This method posts plain JSON to /public/email-check. It is not one of the encrypted and optionally signed ping/fingerprint request paths.

Configuration

Common configuration

OptionTypeBehavior
apiTokenstringRequired API token sent as x-api-key.
serverUrlstring?Defaults to https://api.videntrix.com.
regionIdnumber?Default region for pings; a ping-level value overrides it.
signingSecretstring?Enables HMAC-SHA256 on encrypted ping and server fingerprint requests.
locationStrategy'race' | 'sequential'?Location retrieval strategy; defaults to race.
stopOnPermissionBlockboolean?Stops continuous pinging on a location-permission block when true; defaults to false.

PingOptions

interface PingOptions {
  userId?: string;
  silent?: boolean;
  skipLocation?: boolean;
  source?: string;
  regionId?: number;
  email?: string;
  firstName?: string;
  lastName?: string;
}

Advanced location tuning

Advanced options include locationMaxAge, locationMaxStalenessRadius, locationMaxAgeCeilingMs, locationMaxAgeAssumedSpeedMps, locationRecoveryAfterMs, locationRecoveryMinIntervalMs, locationRecoveryFailureCooldownMs, locationRecoveryMaxAccuracy, and locationFixSendCapMs. Their defaults coordinate caching, movement assumptions, and recovery throttling and should normally remain unchanged. Adjust them only with Kade integration guidance.

Response handling

ping() resolves to PingResponse. A matched area is represented by PingArea { id, name }.

interface PingArea {
  id: number;
  name: string;
}

interface PingResponse {
  inside: boolean;
  status: number;
  message?: string;
  code?: GeoFenceResponseCode;
  interval: number;
  distanceToBorder: number;
  regionId?: number;
  area?: PingArea;
  blocked?: boolean;
  clientId?: string;
}
const response = await sdk.ping({ userId: 'player-123' });

if (response.blocked) {
  handleBlocked(response.code, response.message);
} else if (response.inside) {
  console.log(`Matched ${response.area?.name ?? 'configured area'}`);
} else {
  console.log(`Outside by ${response.distanceToBorder} metres`);
}

Known block response codes

CodeMeaning
BLOCKEDGeneric security or compliance rejection.
BLACKLISTEDThe client is blacklisted.
DEV_TOOLS_DETECTEDDeveloper tools or debugger evidence caused the block.
VPN_DETECTEDVPN, proxy, hosting, or related network evidence caused it.
DEVELOPER_MODE_DETECTEDDevice developer mode or attached-debugger evidence caused it; mainly relevant to the shared mobile contract.
LOCATION_PERMISSION_MISSINGLocation permission was unavailable for verification.

The response type also permits future string codes. Handle unknown codes as blocked when blocked === true.

Request signing

When signingSecret is configured, the SDK applies HMAC-SHA256 only to encrypted ping() and server fingerprint requests.

const sdk = new GeoFenceSDK({
  apiToken: 'your-api-token',
  signingSecret: 'your-workspace-signing-secret'
});

Message and header format

The signed message is timestamp.nonce.JSON.stringify(encryptedPayload). The returned signature header has this format:

X-Signature: t=1708012345678,n=a1b2c3d4e5f6...,s=64_character_hex_hmac
X-Request-Time: 1708012345678

The timestamp and random nonce are generated by the signing function. Signing failures are best-effort and do not change the encryption applied to these payloads.

Client-side secrets are visible to the browser user. Treat request signing as payload-integrity support, not as a way to hide a permanent credential in public JavaScript. Follow your Kade workspace's provisioning and rotation policy.

TypeScript

The script and declaration files are separate downloads. Save the declaration in your project so builds do not depend on a network type reference.

curl -o types/geofence-sdk.d.ts \
  https://sdk.videntrix.com/geofence-sdk.d.ts

Add it to your compilation with a local reference:

/// <reference path="./types/geofence-sdk.d.ts" />

const sdk = new GeoFenceSDK({ apiToken: 'your-api-token' });
const result: GeoFenceSDK.PingResponse = await sdk.ping({
  userId: 'player-123'
});
const localFingerprint: GeoFenceSDK.FingerprintResult =
  await sdk.getFingerprint();
Standard TypeScript does not fetch an HTTPS triple-slash reference. Download the declaration during setup or in your build pipeline, then reference the local file. The CDN script only provides runtime code.

Key exported types

GeoFenceConfig, PingOptions, PingResponse, PingArea, FingerprintResult, FingerprintServerResponse, EvaluateEmailOptions, GeoLocation, and LocationEstimateResult.

Playground

Exercise the bundle loaded by this page. Credentials entered here are used only by the in-page SDK instance.

Waiting for an action.

Best practices

Initialize once

Create one SDK instance and call init() at a predictable startup point. Handle both rejected promises and onError notifications.

Explain location use

Ask from a clear user gesture and provide context before the browser prompt. Handle denial without trapping the user.

Choose a location mode

Decide whether an instance uses browser location or skipLocation before its first ping; the opt-out is sticky for that session.

Clean up

Stop continuous checks when the flow ends and call destroy() when disposing the integration.

Need help with your integration?

Talk to the Kade team about implementation, configuration, or moving your location checks into production.

Contact Kade