glassprint · Guides

Integration recipes

Every recipe follows the same shape: call identify(), read the class plus the additive signals (visitorId, suspectScore, smartSignals, needsAttestation), and feed them into your risk decision. glassprint never auto-blocks — it gives you a server-authoritative read and lets you set the policy.

The visitorId is salted-HMAC, epoch-rotated, and tenant-bound — use it to recognise a returning device within your own tenant, not as a cross-site identity. Pass linkedId to attach your own customer key as event metadata (it is not an identity-graph join).

1. Account-takeover at login

On a login attempt, recognise whether this device has signed in as this account before. A known device with a clean signal set proceeds; a never-seen device, or one carrying automation evidence, gets challenged.

ATO at login
const r = await gp.identify({ tag: 'login', linkedId: accountId });

const seenBefore = await haveSeenDeviceForAccount(r.visitorId, accountId);

if (r.class === 'undeclared_automation') return denyAndAlert();     // additive automation evidence
if (!seenBefore || r.suspectScore > 0.4) return requireStepUp();    // new device or elevated risk
proceed();

2. Multi-account abuse at signup

At signup, look up how many distinct accounts this device has already created. A device minting many accounts is the classic farm signal — but treat the unknown class and farble-flagged privacy users gently, so you never punish a Tor user for being private.

multi-account at signup
const r = await gp.identify({ tag: 'signup' });

if (r.reason === 'consent-denied') return; // no signal was read — fall back to your own controls

const priorAccounts = await accountsForDevice(r.visitorId);

if (priorAccounts >= maxPerDevice && r.class !== 'unknown') {
  return throttleOrReview();   // act on a confident read, not on a refusal-to-guess
}

3. Agent and bot gating

In the AI-agent era you want to allow well-behaved agents and gate the undeclared ones. Use the class directly: a declared_agent (verified Web Bot Auth) gets the agent lane, undeclared_automation is gated, and unknown falls through to your normal flow.

agent / bot gating
const r = await gp.identify({ tag: 'api-gateway' });

switch (r.class) {
  case 'declared_agent':        return allowAgentLane(r);   // still scored — inspect r.suspectScore
  case 'undeclared_automation': return gateOrChallenge();   // hiding as a human
  case 'human':
  case 'unknown':               return proceed();           // never block on 'unknown'
}
declared_agent means authenticated, not trusted — the agent is still scored. Inspect r.suspectScore before granting elevated access.

4. Step-up authentication

When a device is too low-entropy to resolve confidently (an identical stock device), the linker refuses to merge and sets needsAttestation. That is your cue to step up — route to a passkey or another factor rather than guess an identity.

step-up on low entropy
const r = await gp.identify({ tag: 'high-value-action' });

if (r.needsAttestation || r.class === 'unknown' || r.suspectScore > 0.6) {
  return requireStepUp();   // passkey / OTP — don't resolve identity by fingerprint alone
}
proceed();
Honest note on identity resolution: the deployed server resolves with exact-match — repeat sightings are recognised, first sightings score lower. The fuzzy, trained-ML linker (MinHash/LSH + gradient-boosted scorer) exists and is tested but is not wired into the live ingest path yet, so design your recipes around exact-match plus step-up.