Session + Onboarding Guide
Overview
Introduction
Trustfull Session and Trustfull Onboarding address different layers of fraud. Session analyzes the device and browser environment to detect bots, automation, and spoofing. Onboarding cross-references phone, email, and IP signals to verify identity consistency. Used together, they create a layered defense that catches what neither solution can detect alone.
This guide explains how to integrate both products into your signup or registration flow, with two approaches depending on your cost and data requirements.
Want to learn how each product works individually? Visit the Trustfull Session and Trustfull Onboarding product guides.
Below, this guide covers:
- Why combining both products catches more fraud
- Prerequisites and authentication setup
- Two integration approaches: Waterfall (sequential) and Combined (parallel)
- How to combine scores and build decision logic
- Cross-layer risk signals to watch for
Why Combine Both Products?
Each product targets a different class of fraud:
| Threat | Session | Onboarding |
|---|---|---|
| Bots and automated signups | ✅ | — |
| Device spoofing and emulators | ✅ | — |
| AI agents (ChatGPT, etc.) | ✅ | — |
| Headless browsers | ✅ | — |
| Synthetic identities | — | ✅ |
| Stolen phone/email combinations | — | ✅ |
| Disposable phone numbers | — | ✅ |
| Cross-signal inconsistency (email ↔ phone ↔ name) | — | ✅ |
| VPN / Proxy / Tor masking | ✅ | ✅ |
Session validates that the device and browser environment are legitimate. Onboarding validates that the identity behind the device is real. Combining both layers makes fraud significantly harder to scale.
Prerequisites
Before starting, make sure you have:
- A Trustfull account with both Session and Onboarding products enabled
- Your JS key for the Session SDK (client-side, starts with
TFF-orTFB-) - Your API key for backend calls (server-side, used in the
x-api-keyheader)
The JS key and the API key are different credentials. The JS key is safe to expose in frontend code. The API key is a shared secret between your backend and Trustfull and must never be exposed in the browser.
You can find both keys in the App section of your Trustfull dashboard. The JS key appears in the SDK snippet that is pre-filled for you.
How It Works
The integration follows three phases:
- Collect — The Session JavaScript SDK runs in the user's browser, silently capturing device fingerprint, browser signals, and behavioral data (mouse movements, interaction timing). This happens with zero friction.
- Score — Your backend calls the Trustfull APIs to get risk assessments. Session analyzes the device/browser environment. Onboarding cross-references phone, email, IP, and name signals.
- Act — Based on the combined risk picture, your system routes the user through the appropriate flow: approve, step-up verification, or decline.
Session and Onboarding are independent products with separate API endpoints. Your backend is responsible for calling both and combining their results.
Integration Approaches
Trustfull supports two strategies. Choose the one that fits your risk appetite and budget.
Approach 1: Waterfall (Sequential)
Run Session first. Only call Onboarding if the session score falls in the review zone. This avoids unnecessary identity verification costs on traffic that is clearly legitimate or clearly fraudulent.
When to use: Standard signup flows where cost optimization matters. Most customers find that 60–70% of sessions score clearly high or clearly low, meaning Onboarding only needs to run for the remaining 30–40%.
Step 1: Add the Session SDK to your signup page
Place the snippet as early as possible in the page so the SDK has time to collect behavioral signals before the user submits the form.
<script>
(function (f, i, d, o, c, od, e) {
f['FidoObject'] = c;
f[c] = f[c] || function () {
(f[c].q = f[c].q || []).push(arguments);
}, f[c].l = 1 * new Date();
od = i.createElement(d),
e = i.getElementsByTagName(d)[0];
od.async = 1;
od.src = o;
e.parentNode.insertBefore(od, e);
})(window, document, 'script', 'https://det.trustfull.com/det.js', 'tfbd');
tfbd('create', 'your-js-key');
</script>Replace 'your-js-key' with the JS key provided to you.
Step 2: Trigger session data collection on form submit
When the user submits the signup form, generate a unique session ID, send the collected fingerprint to Trustfull, then forward the session ID to your backend along with the form data.
const form = document.getElementById("signup-form");
form.addEventListener("submit", async (ev) => {
ev.preventDefault();
// Generate a unique session ID (max 125 characters)
const sessionId = crypto.randomUUID();
// Send session fingerprint to Trustfull
await tfbd.sendRecord(sessionId);
// Submit form data + sessionId to your backend
await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: document.getElementById('phone').value,
email: document.getElementById('email').value,
first_name: document.getElementById('first_name').value,
last_name: document.getElementById('last_name').value,
session_id: sessionId
})
});
});If the SDK fails to load (e.g., the user has a script blocker or ad blocker),
tfbd.sendRecord()will not be available. Wrap it in a try/catch and decide whether to fail open (proceed without session data) or fail closed (block the signup) based on your risk tolerance.
Step 3: Backend — Check session score, then decide
Your backend retrieves the session score. If the session is clearly trusted or clearly risky, it can act immediately. For borderline sessions, it calls the Onboarding API for identity verification.
Session API — GET https://api.fido.id/1.0/session/result/{sessionId}
import requests
import time
TRUSTFULL_API_KEY = "your-api-key"
TRUSTFULL_BASE = "https://api.fido.id/1.0"
def handle_signup(form_data, session_id):
# 1. Retrieve session results
session = get_session_result(session_id)
if session is None:
# Session SDK did not run — decide based on your policy
return {"decision": "review", "reason": "No session data available"}
# 2. Route based on session score cluster
if session["score_cluster"] in ("high", "very_high"):
return {"decision": "approve", "method": "session_only"}
if session["score_cluster"] in ("very_low", "low"):
return {"decision": "block", "reason": "Session risk too high"}
# 3. Review zone — run Onboarding for identity verification
onboarding = run_onboarding(form_data)
return combine_decision(session, onboarding)The Session API returns the enriched data when ready. If the enrichment is still in progress, it returns a processing status. Poll until the result is available:
def get_session_result(session_id, max_attempts=10):
"""Poll the Session API until enrichment completes."""
for _ in range(max_attempts):
resp = requests.get(
f"{TRUSTFULL_BASE}/session/result/{session_id}",
headers={"x-api-key": TRUSTFULL_API_KEY},
)
if resp.status_code == 404:
return None # Session not found — SDK may not have run
data = resp.json()
if data.get("status") == "processing":
time.sleep(1) # Enrichment still running
continue
return data # Enrichment complete
return None # Timed out waiting for enrichmentOnboarding API — POST https://api.fido.id/1.0/hub
The Onboarding API accepts a customer_id (your unique identifier for the user) and a list of claims specifying which products to run. The score claim produces the cross-product onboarding score and requires at least 2 other claims.
def run_onboarding(form_data):
"""Run Onboarding enrichment."""
resp = requests.post(
f"{TRUSTFULL_BASE}/hub",
headers={
"x-api-key": TRUSTFULL_API_KEY,
"Content-Type": "application/json",
},
json={
"customer_id": form_data["customer_id"],
"claims": ["phone", "email", "ip", "name", "score"],
"phone_number": form_data["phone"],
"email": form_data["email"],
"ip": form_data["ip"],
"first_name": form_data.get("first_name", ""),
"last_name": form_data.get("last_name", ""),
},
)
return resp.json()Available claims: phone, email, ip, name, domain, tax_id, vat_code, browser, device, score. Include whichever claims match the data you collect from your users.
Waterfall: Pros and cons
| Aspect | Detail |
|---|---|
| ✅ Lower cost | Onboarding only runs when needed |
| ✅ Fewer API calls | Clearly good/bad sessions skip Onboarding |
| ✅ Fast for trusted users | High-trust sessions resolve immediately |
| ⚠️ Higher latency for review zone | Sequential calls add up for borderline users |
| ⚠️ Less data on skipped sessions | Sessions that bypass Onboarding have device signals only |
Approach 2: Combined (Parallel)
Run Session and Onboarding simultaneously for every user. Both APIs enrich in parallel, giving you the fullest possible risk picture.
When to use: High-value transactions, regulated industries, or flows where maximum fraud detection accuracy justifies the additional cost.
Steps 1 and 2 are identical to the Waterfall approach: add the Session SDK to your page and trigger sendRecord on form submit.
Step 3: Backend — Run both APIs in parallel
Instead of checking the session score first, your backend fires both requests at the same time and combines their results once both complete.
from concurrent.futures import ThreadPoolExecutor
def handle_signup_combined(form_data, session_id):
with ThreadPoolExecutor(max_workers=2) as pool:
session_future = pool.submit(get_session_result, session_id)
onboarding_future = pool.submit(run_onboarding, form_data)
session = session_future.result()
onboarding = onboarding_future.result()
if session is None:
# Session SDK did not run — decide on Onboarding alone
o_cluster = onboarding.get("score", {}).get("score_cluster", "")
if o_cluster in ("high", "very_high"):
return {"decision": "approve", "method": "onboarding_only"}
return {"decision": "review", "reason": "No session data"}
return combine_decision(session, onboarding)Both approaches reuse the same get_session_result, run_onboarding, and combine_decision functions. The only difference is whether the calls happen sequentially or in parallel.
Combined: Pros and cons
| Aspect | Detail |
|---|---|
| ✅ Maximum data | Every user gets both checks |
| ✅ Fastest overall | Parallel calls reduce total latency |
| ✅ Strongest fraud detection | Cross-layer signals for every session |
| ⚠️ Higher cost | Onboarding runs for every user regardless |
Combining Scores
Both Session and Onboarding return a score on a 0–1000 scale with a score_cluster classification:
| Score | score_cluster | Scorecard Label | Recommendation |
|---|---|---|---|
| 776–1000 | very_high | High | APPROVE |
| 551–775 | high | Good | APPROVE |
| 451–550 | review | Moderate | MANUAL REVIEW / APPROVE |
| 226–450 | low | Bad | DECLINE |
| 0–225 | very_low | Poor | DECLINE |
Curious how scores work? Explore our Scoring Methodology and the Reason Codes that drive them.
When combining both scores, use the following matrix as a starting point:
| Session Cluster | Onboarding Cluster | Recommended Action |
|---|---|---|
very_high / high | very_high / high | ✅ Approve |
very_high / high | review | ⚠️ Review — good device but identity needs attention |
very_high / high | low / very_low | 🚫 Block — possible stolen device or synthetic identity |
review | very_high / high | ⚠️ Step-up — request additional verification |
review | review | ⚠️ Review — manual investigation recommended |
review | low / very_low | 🚫 Block — multiple risk indicators |
low / very_low | Any | 🚫 Block — bot or automation detected |
Here is an example implementation of the combined decision logic:
def combine_decision(session, onboarding):
"""Combine Session and Onboarding results into a single decision."""
s_cluster = session["score_cluster"]
o_cluster = onboarding.get("score", {}).get("score_cluster", "")
trusted = ("high", "very_high")
risky = ("very_low", "low")
# Both trusted — approve
if s_cluster in trusted and o_cluster in trusted:
return {"decision": "approve"}
# Either critically risky — block
if s_cluster in risky or o_cluster in risky:
return {"decision": "block"}
# Mixed signals — review
return {
"decision": "review",
"session_score": session["score"],
"session_cluster": s_cluster,
"onboarding_score": onboarding["score"]["score"],
"onboarding_cluster": o_cluster,
"reason_codes": onboarding["score"].get("reason_codes", ""),
}These thresholds are starting points. Analyze your historical data to calibrate them to your specific fraud patterns and false positive tolerance. Your Trustfull account manager can help with this process.
Cross-Layer Risk Signals
The most valuable fraud patterns emerge when Session and Onboarding signals are read together.
Red flags to watch
| Session detects | Onboarding detects | Risk interpretation |
|---|---|---|
has_automated_browser: true | Valid, established identity | Credential stuffing or account farming with stolen data |
has_spoofed_device: true | Disposable phone + disposable email | Fully synthetic fraud operation |
ip_is_vpn: true | IP country ≠ phone country | Identity does not match claimed location |
has_ai_agent: true | New email, no social footprint | AI-driven synthetic identity creation |
mouse_movement: 0 | Valid identity, strong history | API abuse or bot with stolen credentials |
| All signals genuine | Phone/email are disposable | Real device but throwaway identity — potential drop account |
Trust signals that reinforce each other
| Session detects | Onboarding detects | Interpretation |
|---|---|---|
| Real browser, no spoofing | Consistent phone + email + IP country | Genuine user on a genuine device |
| Residential IP, normal interactions | Email with history, valid phone carrier | Low-risk, established identity |
| Matching user agents, valid video card | Name consistent across phone and email | Strong cross-layer trust |
Best Practices
-
Load the SDK early. Place the Session SDK script at the top of your page, not just before the submit button. The SDK needs time to collect behavioral signals like mouse movements and interaction timing.
-
Use the real client IP. Pass the user's actual IP address to the Onboarding API (from
X-Forwarded-Foror your load balancer's equivalent). This ensures IP-based signals are accurate and consistent with what Session observes. -
Include name when available. Adding
first_nameandlast_nameto the Onboarding call enables name-consistency signals across phone and email, which significantly improves synthetic identity detection. -
Store both responses. Save the full response from both APIs alongside the user record. This creates an audit trail for compliance and helps you tune decision thresholds over time.
-
Start permissive, tighten gradually. Begin with lower thresholds (approve more users) and monitor fraud rates. Gradually tighten as you build confidence in the signal quality for your specific user base.
-
Handle SDK failures. If the Session SDK cannot load (ad blockers, script blockers, network issues),
tfbd.sendRecord()will fail. Your frontend should catch this and still submit the form. Your backend should handle the case where session data is unavailable — either by falling back to Onboarding-only or by flagging for manual review. -
Monitor score distributions. Track the distribution of Session and Onboarding scores for your traffic over time. Unusual shifts may indicate changes in your fraud landscape or integration issues.
📚 Resources
- Support Center — Assistance and answers from the Trustfull team.
- Trustfull Session Guide — How Session works and what it detects.
- Trustfull Onboarding Guide — How Onboarding works and what it detects.
- Session API Reference — Session API technical documentation.
- Onboarding API Reference — Onboarding API technical documentation.
- Session Data Schema — Full list of Session response fields.
- Onboarding Data Schema — Full list of Onboarding response fields.
- Reason Codes — Understanding risk and trust reason codes.
- Scoring Methodology — How scores are calculated.
Updated about 18 hours ago