01 · Device intelligence for fraud prevention The journeys you tag

Device intelligence and fraud risk API for the web Assess every access. Decide with evidence.

One tag per screen you choose

One script on your site reads the browser and the device in about a tenth of a second on desktop. One signed call from your backend, with a tag you name, gets the answer right away: a trust score, evidence coverage and 13 indicators, from bots and automation, browser spoofing and emulators to VPN, proxy and Tor, with the reason behind every recommendation. Spot account takeover, fake signups and payout fraud with evidence, not guesswork. We recommend. You decide and act.

Synchronous response on any screen you tag · Shadow mode before it influences any decision · Your rules, your lists, your call

How to read this response · v1.1/login_completed example

  1. Why this recommendation

    ALLOWreason_codes · NO_ADVERSE_INDICATOR_MATCH

    No indicator the policy weighs came back detected. Contract example: FingerTrackID's managed policy recommends MONITOR at every score; ALLOW comes from a score band your company set in its own risk rules. Decided under policy-login-v1.

  2. What the score is, and is not

    trust_score 1000–100 · higher is better

    Confidence in this access under this policy version, not a fraud probability. Outside COMPLETED it is null, never a default.

  3. How much could be seen

    evidence_coverage 100limitation_codes · none

    The share of the expected checks that ran. Missing evidence would be named here: it lowers coverage and is never a risk reason.

  4. Then it is yours

    customer_actionthen the outcome

    Your backend decides and acts, then reports what it did and the real result. We never block or hold money.

POST /v1/assessments → 201

event-assessment.v1.1 · contract example

assessment_statusCompletedassessment completed
trust_score1000–100 · higher is better
device_match_confidence0.980–1 · strength of the match
evidence_coverage1000–100 · sufficiency
ALLOWrecommendation_basis · EVALUATIONrecommendation, not action
reason_codes NO_ADVERSE_INDICATOR_MATCH  ·  limitation_codes none
{  "schema_version": "event-assessment.v1.1",  "tag": "login",  "assessment_status": "COMPLETED",  "retryable": false,  "trust_score": 100,  "device_id": "dev_0123456789abcdefghij",  "device_match_confidence": 0.98,  "evidence_coverage": 100,  "data_freshness": { "all_required_fresh": true, … },  "signals": [    { "signal_code": "ANTI_FINGERPRINTING", "signal_state": "NOT_DETECTED" },    { "signal_code": "BOT_AUTOMATION", "signal_state": "NOT_DETECTED" },    { "signal_code": "BROWSER_SPOOFING", "signal_state": "NOT_DETECTED" },    { "signal_code": "EMULATED_ENVIRONMENT", "signal_state": "NOT_DETECTED" },    { "signal_code": "PRIVATE_BROWSING", "signal_state": "NOT_DETECTED" },    { "signal_code": "BROWSER_PRIVACY_*", "signal_state": "NOT_DETECTED" }, // ×3    { "signal_code": "NETWORK_PROXY", "signal_state": "NOT_DETECTED" },    { "signal_code": "NETWORK_VPN", "signal_state": "NOT_DETECTED" },    { "signal_code": "NETWORK_HOSTING", "signal_state": "NOT_DETECTED" },    { "signal_code": "NETWORK_TOR", "signal_state": "NOT_DETECTED" },    { "signal_code": "DEVICE_MULTI_USER", "signal_state": "NOT_DETECTED" }  ],  "limitation_codes": [],  "reason_codes": ["NO_ADVERSE_INDICATOR_MATCH"],  "recommended_action": "ALLOW",  "recommendation_basis": "EVALUATION",  "policy_version": "policy-login-v1",  "evaluated_at": "2026-09-04T12:00:01Z",  "expires_at": "2026-09-04T12:05:00Z"  // … 16 more fields in the full response: ids, snapshot, versions, timestamps}
Full response · 54 lines · 13 signals, undetected ones collapsed
1{2  "schema_version": "event-assessment.v1.1",3  "assessment_id": "01996f2a-0000-7000-8000-000000000101",4  "assessment_version": 1,5  "supersedes_assessment_id": null,6  "business_event_id": "01996f2a-0000-7000-8000-000000000102",7  "external_event_id": "login_20260904_000001",8  "tag": "login",9  "assessment_status": "COMPLETED",10  "retryable": false,11  "trust_score": 100,12  "device_intelligence_snapshot_id": "01996f2a-0000-7000-8000-000000000103",13  "device_intelligence_snapshot_version": 1,14  "device_id": "dev_0123456789abcdefghij",15  "device_match_confidence": 0.98,16  "evidence_coverage": 100,17  "data_freshness": {18    "evidence_cutoff_at": "2026-09-04T12:00:00Z",19    "oldest_required_observed_at": "2026-09-04T11:59:59Z",20    "maximum_required_age_ms": 300000,21    "all_required_fresh": true22  },23  "signals": [24    { "signal_code": "ANTI_FINGERPRINTING", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },25    { "signal_code": "BOT_AUTOMATION", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },26    { "signal_code": "BROWSER_SPOOFING", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },27    { "signal_code": "EMULATED_ENVIRONMENT", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },28    { "signal_code": "PRIVATE_BROWSING", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },29    { "signal_code": "BROWSER_PRIVACY_BRAVE", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },30    { "signal_code": "BROWSER_PRIVACY_SAFARI", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },31    { "signal_code": "BROWSER_PRIVACY_SAMSUNG_INTERNET", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },32    { "signal_code": "NETWORK_PROXY", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },33    { "signal_code": "NETWORK_VPN", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },34    { "signal_code": "NETWORK_HOSTING", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },35    { "signal_code": "NETWORK_TOR", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … },36    { "signal_code": "DEVICE_MULTI_USER", "signal_state": "NOT_DETECTED", "source": "DERIVED", "detector_version": "detector-web-v1", … }37  ],38  "limitation_codes": [],39  "reason_codes": ["NO_ADVERSE_INDICATOR_MATCH"],40  "recommended_action": "ALLOW",41  "recommendation_basis": "EVALUATION",42  "policy_version": "policy-login-v1",43  "fallback_policy_version": "production-observation-fallback-v2",44  "ruleset_version": "ruleset-web-v1",45  "signal_catalog_version": "signals-web-v1",46  "feature_schema_version": "features-v1",47  "resolver_version": "resolver-v1",48  "sdk_version": "web-sdk-1.0.0",49  "provider_versions": ["network-provider-2026-09-04"],50  "evidence_cutoff_at": "2026-09-04T12:00:00Z",51  "evaluated_at": "2026-09-04T12:00:01Z",52  "expires_at": "2026-09-04T12:05:00Z",53  "correlation_id": "corr-assessment-login-0001"54}

Versions that shaped it · policy-login-v1 · production-observation-fallback-v2 · ruleset-web-v1 · signals-web-v1 · features-v1 · resolver-v1 · web-sdk-1.0.0 · network-provider-2026-09-04

13 signals in the full response · indicator matrix ↓

The product in numbers

The response

13

Web indicators in every response

3

States per indicator: detected, not detected, not evaluated

14

Named reasons for "not evaluated", never a silent false

5

Possible recommendations

3

Separate measures: trust, device match, evidence coverage

1

Signed call per assessed event

The collection

17

Sections of browser and device evidence per collection

48

Browser APIs checked against the version the browser claims

125

Up to 125 ms: median desktop collection in Chrome, Firefox and Safari

0

Permission prompts shown to your user

Your rules and your investigation

54

Context insights computed inside every assessment

67

Rules your company can weigh in its own score: 13 indicators and 54 insights

6

Published reference lists behind the network indicators

6

Console languages: English, Portuguese (Brazil and Portugal), Spanish, French, German

Never — and this does not change by plan

  • Pointer movement and keystrokes: the SDK collects neither
  • Pixels, audio samples, GPU vendor and renderer names or font names: canvas, WebGL, WebGPU, audio and fonts leave the browser only as hashes, a GPU family and a presence mask over a fixed font list
  • WebRTC, local ports, battery, motion sensors: never read
  • No permission prompt, no decision made in the browser, no action taken by us on your account

— Every value is a count of a versioned contract, catalog or list, except the collection time, measured on our test machines with a cold cache. None of them changes by plan or volume.

03 · Use cases

Fraud prevention on the screens where the decision matters.

Device intelligence answers one question before your backend lets the action through: what is really on the other side of this screen? The same call serves every journey; the tag you send only files it.

login · password_recovery · profile_change

Account takeover prevention

A known account on a device it has never used, a browser that lies about itself, an anti-detect browser, travel that cannot be done in time. Each comes back as evidence, so you ask for a second factor only when it matters.

signup · login · checkout

Bot and automation detection

Headless browsers, automation frameworks, AI agents identified by signature and traffic from datacenters. BOT_AUTOMATION and NETWORK_HOSTING say what was seen; coverage says how much could be seen.

signup · deposit · promotions

Fake accounts and multi-accounting

Accounts and people per device and per IP, how rare the device profile is in your base, one collection repeated on another device. Context your rules can weigh, never an automatic verdict: one person can have several legitimate accounts.

withdrawal · pix_transfer · trade

Payout, Pix and transfer fraud

A withdrawal in the first hour after a new device, beneficiaries shared across accounts, neighbors you marked as confirmed fraud. Your backend holds or reviews the money; we never do.

any journey

Emulators and spoofed browsers

Android emulators and virtualization, a GPU impossible for the declared system, a browser version its own APIs contradict. Device fingerprinting signals read as hashes and families, never as raw pixels.

any journey

VPN, proxy and Tor detection

Known VPN providers, public proxies, Tor exits, hosting ranges and Apple iCloud Private Relay, from published lists refreshed on our side. Anonymization is context, not guilt: your policy decides how much it weighs.

04 · The measures

Separate measures. No average disguised as certainty.

Merging confidence, coverage and device match into a single number hides exactly what the analyst needs to see: whether the problem is the access or the evidence.

trust_score

Access confidence

72

0100 · higher is better

Answers how trustworthy this access is under this policy version; the tag you send does not change it. Integer from 0 to 100. Exists only when assessment_status = COMPLETED; in every other state it is null, never a default value.

Not a fraud probability, the person's reputation or a default for missing data. We show 72/100, never "72% trustworthy".

device_match_confidence

Device match

0.94

01 · cap 0.98

Strength of the evidence that this access comes from the same device already seen, from 0 to 1. Comes with device_id, an opaque public identifier of the probabilistic cluster.

Not the person's identity, and not proof of hardware. Party and Account are different things.

evidence_coverage

Evidence sufficiency

100

0100 · 13 of 13 evaluated

How much of the expected set of checks could be run, from 0 to 100. Every applicable indicator left as NOT_EVALUATED lowers coverage and shows up in limitation_codes.

Not part of the score. Low coverage neither drags the score down nor pushes it up: it leaves the assessment inconclusive.

Why we do not add them up

A single "suspicion" score mixes three different questions: does this access deserve trust? Was the evidence sufficient? Is it the same device? When the answer comes separated, your analyst knows whether to ask for a second factor, wait for collection or look at the history. When it comes fused, all they know is that "it scored 42".

05 · 13 indicators

Thirteen indicators. Each one says whether it saw, did not see, or could not evaluate.

Every web assessment returns the 13 codes, always. What changes is the state of each one, never the list. Each indicator has its own versioned detector, and the response names the version that evaluated it. A detector that is missing, expired or failing never becomes false.

signal_catalog web · 13 codes · withdrawal_completed example

  • Detected
  • Not_detected
  • — Not_evaluated

Environment integrity

Browser privacy · neutral context

Network · neutral context

Device

reason_codes · NETWORK_TOR_DETECTED REVIEW

In our managed policies, Tor alone never justifies BLOCK_RECOMMENDED; rules your company configures follow the weights and cutoff it chose. In this v1 contract example, policy-withdrawal-v1 asked for review of the withdrawal, not a block, and the single reason code says exactly that. The managed policy in production today recommends MONITOR at every score.

The same indicator, three states

  • Detected · NETWORK_TOR PROVIDER · ftid-network-tor.v1 · 0.9 · withdrawal_completed example
  • Not_detected · NETWORK_TOR PROVIDER · ftid-network-tor.v1 · 0.9 · login_completed example
  • — Not_evaluated · NETWORK_TOR not_evaluated_reason_code PROVIDER_UNAVAILABLE · limitation_codes ["PROVIDER_UNAVAILABLE"] · evidence_coverage 69 · provider_unavailable example

A detector that is missing, expired or failing never becomes false. NOT_APPLICABLE comes only from the catalog's immutable matrix by channel, engine and platform; your tag never changes it, and a policy turned off, a timeout or an outage never take that shortcut.

Neutral context by default

PRIVATE_BROWSINGBROWSER_PRIVACY_BRAVEBROWSER_PRIVACY_SAFARIBROWSER_PRIVACY_SAMSUNG_INTERNETNETWORK_PROXYNETWORK_VPNNETWORK_TOR

They can limit what we observe. Coverage falls only where evidence is not evaluated. They are not fraud. They describe the environment; they do not accuse the person. In our managed policies, on their own they never justify BLOCK_RECOMMENDED; rules your company configures follow the weights and cutoff it chose. When the type of network cannot be told apart, as behind Apple's iCloud Private Relay, hosting, VPN and proxy all stay NOT_EVALUATED with NETWORK_TYPE_AMBIGUOUS: we never mark three detections from a single boolean.

Network evidence comes from published reference lists that we refresh on our own infrastructure: Tor exit addresses, cloud provider ranges, hosting-network lists, VPN provider ranges and a public-proxy database. Your users' addresses are never sent to the list publishers. A list past its maximum age leaves its indicator NOT_EVALUATED, never NOT_DETECTED.

06 · Context insights

Beyond the 13 indicators: 54 context insights. For the investigation, and for your score when you choose.

Inside every assessment, before the response, we compute 54 context insights from what was collected and from your company's own history: who else uses the device, how fast the account is moving, from which network, next to which accounts. FingerTrackID's managed policy keeps them out of the score. In your own risk rules, any of them can be off, ghost or active, with points for unusual and very unusual: an active insight that fires takes points off the trust score and names itself in reason_codes.

context-insight.v1 · 54 insights in six themes · as grouped in the console and in your rules

  • Account and device · 10

    New device and new operating system for the account · distinct accounts and people on the device over 30 days · accounts of the same person on it · devices per account and per person over 24 hours · high-activity device · how common, uncommon or rare the device profile is in your company · device cookie lost

  • Withdrawals and orders · 7

    Withdrawals in the first hour after a new device · beneficiaries per device and accounts per beneficiary over 30 days · withdrawal count and amount per account · order count per account and the median interval between orders

  • Network and location · 12

    New country and new internet provider (ASN) for the account · distinct countries, providers and IPs per account · accounts and people per IP · impossible travel · browser time zone that does not match the IP's country · VPN egress country · provider on a public abuse list · Apple iCloud Private Relay

  • Browser and SDK integrity · 16

    Developer tools open · automation marks · anti-detect browser marker · TLS signature that does not match the declared browser, and its stability over 30 days · software renderer and GPU family · canvas or audio that changes between two reads · in-app browser · possible guest mode · AI agent identified by signature · bot category · SDK different from the approved package · one SDK session on several devices · identical collection repeated on another device · device cookie used in places too far apart

  • Velocity · 5

    Accesses per device and per account over 5 minutes, 1 hour and 24 hours; per IP over 5 minutes and 1 hour · assessments with the same tag per account and per device over 1 minute, 5 minutes and 1 hour

  • Links between accounts, one hop · 4

    Other accounts that used the same devices · neighboring accounts where automation or browser spoofing was detected · neighboring accounts you marked as confirmed fraud · withdrawal beneficiaries shared with other accounts

Three states · a reason, never a zero

  • Observed · the condition is present, or the count or measure differs from its neutral value.
  • Not observed · evaluated, and absent or neutral.
  • Not evaluated · always with its reason, never shown as zero.

Some insights depend on what you send. Without account or person refs, without a tag, or without outcomes, the item says so: account not sent, assessment without a tag, no outcomes sent. A tagged request carries no amount or beneficiary, so the withdrawal amount and beneficiary items say that too. A first access, an anonymized network or an IP outside the one-hour window of a check are reasons as well.

What they are not

  • Not one of the 13 indicators, and never a limitation code. They reach reason_codes only through a rule your company made active.
  • Never in the score of FingerTrackID's managed policy. An active insight without data takes nothing off: missing evidence never becomes risk.
  • Not a hidden model: each insight is read as expected, unusual or very unusual against a ruler your company can adjust, starting from a published default.
  • Not an accusation: a high count of accounts or people on one device is not evidence of fraud, and there is no "N accounts per device = fraud" rule.

Where the analyst reads them

Access detail. An "Access context" card groups the insights by theme, with their time windows, the cutoff of the history they considered, how unusual each one is on your ruler and the reason for every item left without a value. An insight that does not fit the assessment's time budget is finished in the background and says so; it never delays or fails the response.

Indicator catalog. One page explains what each of the 13 indicators looks for, what leaves it not evaluated and whether it can move the score, and lists the context insights with the reasons they can stay without a value.

Canvas, WebGL, audio and fonts are read under the managed collection policy of every company, as derived values only, and feed a few of these insights, such as the GPU family, a software renderer or a render that changes between two reads. Where the browser does not allow a check, the item stays not evaluated, with its reason.

07 · Inconclusive is a result

When we cannot assess, we say so, and why.

Missing evidence does not produce confidence 100, and it does not become a fraud signal. With no score, the recommendation comes from the fallback frozen inside the policy version and covered by its hash, and it is one answer for every scoreless state, whatever the tag: MONITOR. Never ALLOW.

Scoreless cases from the contract examples

The tag never selects the fallback. In the managed policy every state without a score, on any tag, recommends MONITOR: carry on and record it. So do the three contract examples below.

  • INCONCLUSIVEtag login · SDK did not load

    MONITOR

    No collection: trust_score: null, evidence_coverage: 0, COLLECTOR_NOT_LOADED, basis INSUFFICIENT_EVIDENCE. Nothing is ever released as ALLOW.

  • INCONCLUSIVEnetwork evidence unavailable

    MONITOR

    Four network indicators NOT_EVALUATED, evidence_coverage: 69, PROVIDER_UNAVAILABLE. Whatever the operation, it stays in your system, under your decision; we never hold money.

  • FAILEDtag pix_transfer · policy unavailable

    MONITOR

    Technical failure: POLICY_UNAVAILABLE, policy_version: null, basis TECHNICAL_FAILURE. Never read as "no risk".

With no score we never recommend ALLOW · fallback_policy_version production-observation-fallback-v2 in the examples · the fallback is validated with the policy every time the policy is read

Four assessment states

Completed 72 basis EVALUATION · reason_codes explain the score
Inconclusive null basis INSUFFICIENT_EVIDENCE · limitation_codes say what was missing
Unscorable null basis OUT_OF_POLICY_SCOPE · valid event, policy does not cover it
Failed null basis TECHNICAL_FAILURE · POLICY_UNAVAILABLE, policy_version null · never "no risk"

The catalog's 14 limitation codes · what was missing, never why it is risky

NO_FIRST_PARTY_COOKIEINSTALLATION_COOKIE_REJECTEDNO_EDGE_SIGNALSSTALE_DEVICE_PROFILEPROVIDER_UNAVAILABLEPROVIDER_BUDGET_EXCEEDEDCOLLECTOR_NOT_LOADEDUNSUPPORTED_RUNTIMEREQUIRED_EVIDENCE_MISSINGDETECTOR_TIMEOUTDETECTOR_ERRORNETWORK_TYPE_AMBIGUOUSCLIENT_CLOCK_SKEWPOLICY_UNAVAILABLE

A limitation code explains collection and quality; a reason code explains policy. Every indicator NOT_EVALUATED points to its own; the assessment aggregates them all in limitation_codes. A collection limitation is never a risk reason.

08 · You decide

Five recommendations. Zero actions taken by us.

recommended_action says what the policy suggests. customer_action says what you did. Two fields because they are two facts.

Who recommends, who decides, who acts

FingerTrackID recommends

  • ALLOW
  • MONITOR
  • STEP_UP
  • REVIEW
  • BLOCK_RECOMMENDED

Never from privacy alone · Tor, VPN, proxy, incognito, shared device

Your flow decides

Your backend decides and acts. We return a recommendation; today the managed policy answers MONITOR, observation only. What happens to the access is decided and executed on your side. You know the account, the limit, the history and the cost of friction. We do not.

You act and report back

  • ALLOWED
  • CHALLENGED
  • HELD
  • REVIEWED
  • BLOCKED
  • CANCELLED

customer_action · the field you send back

  • We never authenticate a person.
  • We never block your interface.
  • We never move money.

What each recommendation does not mean

  • ALLOW

    The policy found no reason for friction on this access. Does not mean that the person is legitimate or that the access is certified. It is only issued with a score and the required evidence evaluated.

  • MONITOR

    Carry on, but record it. It is the scoreless fallback on every tag, and today the managed policy's answer at every score too. Does not mean confirmed suspicion, and it does not require any action from the user.

  • STEP_UP

    Ask for a second factor or a confirmation. Does not mean that we challenge anyone: you are the one who shows the challenge.

  • REVIEW

    Send it to human review. Does not mean that we hold money: the operation stays in your system, under your decision.

  • BLOCK_RECOMMENDED

    The policy suggests denying. Does not mean a block carried out, fraud confirmed or a ban. When you block, that becomes customer_action: BLOCKED, an event of yours, and it feeds the accuracy measurement.

09 · Integration

Three steps, one contract. The browser collects; your backend asks; we answer.

One script on your site, one server-to-server endpoint in your backend and two calls to send back the action and the result. No decision on the client.

SDK on your site

The SDK collects and returns a token. That is all.

Served from a subdomain of yours. Before you send the request you want assessed, whatever the screen, it collects browser and environment signals and receives 202 PROCESSING: it confirms receipt, not a result. Network signals are added by the edge on your collection domain, never by the script. No score, signal or decision goes back to the browser.

import { initializeSdk } from '@ftid/sdk-web'

// publicKey is a public locator, not a secret.
// collectionOrigin is YOUR collection domain (https).
const sdk = initializeSdk({
  publicKey: 'pub_app_0123456789abcdef',
  collectionOrigin: 'https://collect.yourcompany.com',
})

// ACCEPTED (202 PROCESSING on the wire): confirms receipt, not a result.
const collection = await sdk.collect()
if (collection.status !== 'ACCEPTED') {
  // Without collection the journey continues; the backend will get
  // INCONCLUSIVE / COLLECTOR_NOT_LOADED: no fake score.
  return continueWithoutToken()
}

// Create the event before sending the request to be assessed.
const event = sdk.createEvent(collection.observation)

// Only a COMPLETED collection yields a token. The token goes in the POST
// that YOUR backend makes; the decision never comes back here.
const ctx = await sdk.getEventContext(event)
const token =
  ctx.status === 'COMPLETED'
    ? ctx.eventContextToken
    : null

Your backend calls

One signed, synchronous call, on your deadline.

HMAC-SHA-256 over ten canonical lines — marker, key_id, method, path, timestamp, event and request ids, content metadata, and the SHA-256 of the exact body bytes on the last line. Never re-serialize the JSON before hashing; the contract carries the normative vector. key_id is the only public locator. The secret never travels. Canonical vector requests.both_refs_deadline_750 from the v1.1 contract: the same route and the same signature as v1, with a tag instead of journey, action and journey_context.

POST /v1/assessments HTTP/1.1
Host: api.fingertrackid.com
Content-Type: application/json
X-FTID-Signature: v1=<key_id>:<mac-hex>
X-FTID-Timestamp: 1791720000
X-FTID-Request-Id: <uuidv7 of the attempt>
X-FTID-External-Event-Id: evt:tag-0003

{
  "schema_version": "event-assessment-request.v1.1",
  "external_event_id": "evt:tag-0003",
  "idempotency_key": "idem-tag-0003",
  "occurred_at": "2026-10-11T12:00:00.000Z",
  "subject_ref": "subject:synthetic",
  "account_ref": "account:synthetic",
  "event_context_token": "ctx.synthetic-token_0003",
  "tag": "pix_transfer_a1b2c3d4e5f6g7h8i9j0abcdeabcdeabcdeabcdeabcdeabcdez",
  "deadline_ms": 750
}

tag is a lowercase slug of up to 64 characters (letters, digits and underscore, at most ten digits), never personal data; this vector sits at that limit · subject_ref and account_ref may be null, and an account_ref requires a subject_ref · refs are opaque codes of yours, never a national ID, email or sequential ID · deadline_ms is an integer from 250 to 5000, or null for 5000; if it is exceeded, a retryable 503 Problem comes back, not a partial score

Synchronous response

Read it, decide, send back what you did.

status
COMPLETED
trust_score
72
device_match
0.94
coverage
100
reason_codes
NETWORK_TOR_DETECTED
limitations
none
policy
policy-withdrawal-v1
expires_at
+5 min
REVIEW

Then two feedback calls: customer_action with what you did and, once you know, truth_status with the real result (the outcome: legitimate, fraud confirmed or insufficient evidence). The contract holds both to the same discipline as the assessment call: nothing counts as accepted before a durable commit, and an identical retry adds no second effect. An outcome can be corrected later — the correction is appended as a successor, and the earlier record stays readable.

POST /v1/assessments/{assessment_id}/customer-actions
POST /v1/assessments/{assessment_id}/outcomes

↑ back to the full example

  • Web SDK
  • Synchronous S2S API
  • Server SDK for Node.js
  • MCP server for coding agents
  • Action and outcome API
  • Console

Web, in any modern browser · no app to install · SDK, assessment and feedback in the same versioned contract

npm install @fingertrackid/server signs the call for you, and @fingertrackid/mcp lets Claude Code, Cursor or any MCP-capable coding agent wire the integration from the published contract. Step by step in the quickstart, the Web SDK guide and the API reference.

10 · Zero trust in the browser

The browser only collects. Decision and identifier never pass through it.

Nothing coming from the browser is treated as authority. The assessment is server to server, signed, and each attempt counts once: repeating it creates no new effect.

Who talks to whom

Browser Your backend FingerTrackID event_context_token (that is all) HMAC · request_id assessment no decision no device_id

The browser receives only opaque session and event IDs. Score, signals and recommendation are born and stay on the server. The first-party cookie is signed, HttpOnly, Secure and scoped to your tenant and application.

Anatomy of the call

POST /v1/assessmentssecurity ftidHmacV1 from the contract
X-FTID-Signature: v1=<key_id>:<mac-hex>key_id UUIDv7 is the only locator; tenant, application, environment and capabilities only become authority after a valid MAC in constant-time comparison. X-Client-Id, legacy nonce and multiple signatures are refused.
X-FTID-Timestamp: 1791720000Epoch in seconds. Tolerance of ±300 s. A future timestamp never extends retention.
X-FTID-Request-Id: <uuidv7>One use per attempt, reserved after a valid MAC. A logical retry signs a new request_id; an invalid MAC creates no reservation.
X-FTID-External-Event-Id: evt:tag-0003Byte for byte identical to the one in the body and in the signed string.
body.idempotency_keySame key, same operation: an identical retry returns the same persisted assessment, without recomputing.
body.deadline_msYour budget. Exceeded before the assessment: Problem assessment-unavailable (503, retryable: true), not a partial score.
  • 401uniform: key_id that does not exist, was revoked, or a wrong signature all produce the same authentication-failed.
  • 403an authenticated credential without the route's assessment capability.
  • 503secret dependency unavailable: a global latch makes the response uniform without revealing whether the key_id exists. Sign a new request_id.

11 · Versioned policy

Every decision has a version. The version freezes everything that shaped it.

Changing the sensitivity of one indicator creates a new version, with author, effective period, simulation and rollback. History does not change. A missing real version blocks use.

Version that decided · withdrawal_completed example

policy-withdrawal-v1

fallback_policy_version
production-observation-fallback-v2
ruleset_version
ruleset-web-v1
signal_catalog_version
web-detectors-v3
feature_schema_version
configured-assessment-features.v1
resolver_version
first-party-installation-v1
sdk_version
0.1.0
provider_versions
asn-reputation.v1 · cloud-ranges.v1 · icloud-private-relay.v1 · ip2proxy-lite-px2.v1 · tor-exit.v1 · x4b-vpn.v1
evidence_cutoff_at
2026-09-04T12:00:00Z
evaluated_at
2026-09-04T12:00:01Z
expires_at
2026-09-04T12:05:00Z
correlation_id
corr-assessment-withdrawal-0001

correlation_id is for support without exposing a personal identifier. The exposed versions are opaque public tags; internal database IDs never appear in the API, in the webhook or in the console.

What a policy version freezes

  • signal catalog
  • normalization
  • features
  • device resolver
  • rules
  • correlation strategy
  • levels per indicator
  • caps and thresholds

Reproducible, never rewritten

login_completed example

assessment_version1

supersedes_assessment_idnull

login_completed_revision_2 example

assessment_version2

supersedes_assessment_id…0000000101

An operational revision preserves the policy and every other version in force at the event's received_at. A new policy over an old event is a separate simulation; it never rewrites which recommendation guided the historical decision.

12 · Shadow mode

In parallel first. Only then does the response influence an action.

Shadow mode is running FingerTrackID side by side with what you use today, without the response touching any decision. You compare, send back the real results, and only then start using the recommendation. This is how we recommend starting.

The full cycle

  1. 01

    The SDK collects

    202 PROCESSING · event_context_token

  2. 02

    Your backend asks for the assessment

    POST /v1/assessments · synchronous

  3. 03

    You decide through your flow

    in shadow, without using the response

  4. 04

    You report the action

    customer_action · POST …/customer-actions

  5. 05

    You report the result

    truth_status · POST …/outcomes

recommendation_alignment · derived on the server from the recommendation × action pair; never declared by the client. Coincidence proves neither causality, nor accuracy, nor fraud.

Comparison, field by field

recommended_actioncustomer_actionrecommendation_alignment
REVIEWREVIEWED or HELDALIGNED
STEP_UPALLOWEDNOT_ALIGNED
ALLOW or MONITORALLOWEDALIGNED
BLOCK_RECOMMENDEDBLOCKEDALIGNED
anyCANCELLEDNOT_COMPARABLE

Blocking does not prove fraud. Confirming fraud requires independent evidence: truth_status goes from PENDING to LEGITIMATE, FRAUD_CONFIRMED or INSUFFICIENT_EVIDENCE, with maturity PROVISIONAL or MATURE. Only a mature label becomes a quality metric.

What the cycle feeds

  • False positive and false negative as metrics derived from mature labels, not typed-in fields.
  • Recalibration of rules and thresholds, always as a new policy version.
  • Detector quality measured in your shadow mode, before the response influences any action of yours.
  • What it does not do: become automatic enforcement. customer_action and truth_status are separate facts, and every correction creates a successor.

13 · Privacy by contract

Bounded by the contract, not by a setting.

Cleartext PII is not a product requirement. You send opaque person and account refs; we do not ask for a national ID, email or phone number to assess an access.

packages/contracts/src/sdk-handshake-v1.ts · collection policy

canvas: z.boolean(),
audio: z.boolean(),
fonts: z.boolean(),
behavioral: z.literal(false),

packages/contracts/src/sdk-signals/rendering-v1.ts · excerpt

const Hash128 = z.string().regex(/^[0-9a-f]{32}$/)
canvas_hash: Hash128.optional(),
webgl_gpu_family: z.enum(SDK_RENDERING_GPU_FAMILY_V1).optional(),
audio_hash: Hash128.optional(),
fonts_count: z.number().int().min(0).max(SDK_RENDERING_FONT_COUNT_V1).optional(),

Behavioral collection, meaning pointer movement and keystrokes, only accepts the off value: turning it on requires changing the contract, not a setting. Canvas, WebGL, offline audio and font presence are read under the managed collection policy, which turns them on for every company, and they leave the browser only as derived values: fixed-length hashes, a closed GPU family, a count and a presence mask over a fixed font list. The contract has no field for pixels, audio samples, GPU vendor or renderer names, or font names. Together, these derived values can recognise the same browser again, so they are treated as device data, not as anonymous data. Never collected, as an absolute rule: password, typed text, chat or document content, clipboard, screenshot, microphone, camera. Every piece of evidence has a privacy class, and class is not risk weight.

Guarantees that do not depend on good will

  • PII does not enter logs. Raw data does not appear freely in the console: every access to it is recorded. An identifier or personal value never goes in a URL.
  • Cross-tenant correlation is off. Every domain table carries tenant_id with isolation forced in the database. There is no reputation or label shared between customers.
  • First-party cookie served from your domain: signed, HttpOnly, Secure, limited to your tenant and application. The value never travels to a database, queue or log; if it is missing or rejected, continuity degrades as a limitation and the assessment does not stop.
  • Every device merge is reversible and records the resolver version that made it. IP never consolidates devices on its own.
  • Every kind of evidence has a declared purpose, privacy class, access rule and retention in our data inventory. Legal basis, data residency and the DPA are handled with your legal team at contracting; nothing on this page is legal advice.

Party is not Account

Party Account Account Account

The link comes from your backend · never inferred from fingerprint

One person, several legitimate accounts. An account never changes person silently; the session only progresses from anonymous to person and then to an account of the same person. A contradiction is a conflict, not a new association and not improvised negative evidence.

Where the data lives

cloud
Google Cloud, us-central1 region
source of truth
PostgreSQL, single
isolation
RLS forced per tenant, with a negative test
secrets
Secret Manager; never in code or in infrastructure state
logs
no PII; raw data never displayed freely

14 · Console

A console to integrate, investigate and close the loop.

The console answers "are we receiving, assessing and explaining with quality?" first, and only then lets anyone talk about legitimacy. Every percentage names its denominator.

Schematic, not a screenshot · console tones inside the frame

Access detail. The three measures as three separate components; the recommendation with its recommendation_basis, the policy in force and what your team reported doing; evidence freshness; the 13 indicators with state, applicability, source, detector confidence, freshness and the reason for any not evaluated; limitations and reason codes; session, device and their associations; multiplicity on the device; an approximate location from the network; the access context; and a technical data tab with everything the SDK and the edge collected, including the real browser version read from the APIs it exposes, beside the version it declares.

Investigation filters. The access list narrows by internet provider (ASN), network country, device installation, the reason an indicator was not evaluated, and up to two indicators in chosen states at the same time, and it finds an access by your own account or person ref. There is no filter by IP address: it is personal data. Each access detail also carries its context insights ↑.

Investigate in depth. From any access, a link graph opens the devices and accounts around it, one hop at a time, over up to 92 days of your own history. Every expansion is a recorded read; IP addresses never become nodes.

Block and allow lists. From the access detail, your team puts a device, an account ref or a single IP on your company's block list (block or review) or allow list, with a note and the name of whoever added it. On a scored production access, an entry overrides the recommendation and names itself in reason_codes. Nothing is deleted: removing an entry records who removed it and when.

AI assistant. A side panel explains every screen and answers questions about your own integration and accesses, within your company and your role.

Risk configuration. Your company's own risk rules, per application: each of the 13 indicators is off, ghost or active, with a weight from 1 to 100 points, and you set the score bands and the action of each one. A ghost rule is computed and recorded but never changes the response. Before you publish, the impact card replays your draft over the assessments of the last 1, 7 or 30 days. Each of the 54 context insights ↑ can join them with its own points. Your administrator or risk supervisor publishes directly; every publication is a new audited version, and everything to ghost stops all your rules at once. FingerTrackID never activates a rule for you: its support can only put every rule of an application in ghost, with a reason your console shows. While no rule is active, the managed policy answers, published by the FingerTrackID team with recorded approval.

Sixteen screens in four groups · main, detection, operation, administration

  • 01Home
  • 09Integration and health
  • 02Overview
  • 10Usage
  • 03Accesses
  • 11Exports
  • 04Review queue
  • 12Reports
  • 05Risk configuration
  • 13Users and access
  • 06Policy simulations
  • 14Audit
  • 07Lists
  • 15Customer lifecycle
  • 08Indicator catalog
  • 16Account and security

The review queue is the withdrawal review module: an operational queue built on the same core, which brings every v1 withdrawal recommended for REVIEW to a person, with the evidence alongside. Tagged assessments never enter it, and it stays empty while the policy recommends MONITOR, as the managed policy does today.

In English, Portuguese (Brazil and Portugal), Spanish, French and German. Sign-in with passkey or second factor; permissions by role; an audit trail of who saw sensitive data and who changed what. Raw data does not appear freely: every access to it is recorded.

15 · How we charge

One billing unit. Everything else does not count.

Monthly plans by volume of completed collections, from $59.90 for 20,000, and a free trial to start. Shadow mode consumes the same units.

The unit

One production device intelligence snapshot, requested by your integration and completed.

It is the unit of measurement and the candidate billing unit. Each device_intelligence_snapshot_id produces at most one unit, no matter how many assessments, tags, providers or reprocessings use that collection.

  • Score, recommendation, action and outcome do not determine billing. A risky result does not cost more; a legitimate result does not cost less.
  • A commercial limit never becomes a block, a rate limit or automatic degradation of the service.
  • Every requested snapshot produces an immutable usage record, even when it is not billable. Exclusions are proven by that record, not reconstructed from logs.

Event · creates a unit?

  • Non-production applications (development, staging)no
  • Retry with the same idempotency_keyno
  • Any assessment, on any tag, that reuses a fresh snapshotno
  • Reprocessing, historical replay, policy changeno
  • Asynchronous enrichment and refresh started by usno
  • Provider call or retryno
  • Webhook deliveryno
  • customer_action and outcomeno
  • Query, dashboard, report, exportno
  • 4xx for authentication, authorization or an invalid contractno
  • 5xx, a timeout on our side, collection INCONCLUSIVE or FAILEDno
  • Production snapshot, requested by the integration, completedyes · 1

Seven plans from 20,000 to 1,000,000 a month; Enterprise above that.

See pricing

16 · FAQ

The questions a head of risk and a CTO ask.

Q01How does the integration start?
In shadow mode. You install the SDK on your site, turn on the server-to-server call, and the response starts running alongside your real flow without touching any decision. You send back customer_action and the outcome, and in the console you compare, access by access, the recommendation with what your team reported doing, and follow evidence coverage. When the numbers add up, your team decides whether and where the recommendation starts influencing a decision, always executed by your backend.
Q02Which journeys do you assess?
Any screen you choose. Your backend sends a tag of your own with each assessment, a lowercase slug such as login, withdrawal or pix_transfer: no registration, no closed list and no limit. The console filters, groups and exports by tag, and the templates (signup, login, password recovery, second factor, profile change, deposit, withdrawal, Pix transfer, trade, checkout) are an integration guide. The tag never selects the policy, the detectors, the fallback or the deadline: every tagged assessment runs under the same policy and the same 13 indicators. It must never carry personal data. The request carries no amount, currency or beneficiary.
Q03Why do you not block?
Because an event is not fraud, a score is not a decision, and blocking proves nothing. You are the one who knows the account, the limit, the history and the cost of friction. We deliver evidence, coverage and an explained recommendation. BLOCK_RECOMMENDED is the strongest recommendation there is, and it is still a recommendation. When you block, you record customer_action: BLOCKED, and that becomes data to measure whether the recommendation was right.
Q04Do VPN, Tor or a private tab drag the score down?
In our managed policies, none of them produces BLOCK_RECOMMENDED on its own; rules your company configures follow the weights and cutoff it chose. Private mode and browser protections are neutral context: they may reduce coverage and show up in the detail, with no adverse reason code. Tor, proxy, VPN and hosting are anonymization context: a policy can weigh them with what else was seen, never by tag. In the managed policy today only automation and browser spoofing move the score, and the recommendation is MONITOR. In the v1 withdrawal contract example on this page, Tor detected led to REVIEW, and the reason code says so. It is never fraud in itself.
Q05What if the SDK does not load, or the user blocks scripts?
The journey continues. Your backend calls the API without event_context_token and receives INCONCLUSIVE, trust_score: null, evidence_coverage: 0, limitation_codes: ["COLLECTOR_NOT_LOADED"] and the fallback recommendation, MONITOR. Never an invented score. Never ALLOW for lack of evidence.
Q06What is the latency? Is there an SLA?
The deadline is yours. The contract default is 5000 ms, and deadline_ms sets it anywhere from 250 to 5000 (keep your HTTP timeout above about 6 s for the default); if it is exceeded, you get a Problem assessment-unavailable (503, retryable: true, with Retry-After), not a partial response. Repeat the same logical operation with a new request_id. Latency and SLA go into the proposal, measured in your shadow mode before any decision depends on the response.
Q07How do I know the response was not forged?
It never passes through the browser. Your backend signs every call with HMAC-SHA-256 over ten canonical lines, the last of which is the SHA-256 of the exact body bytes; key_id UUIDv7 is the only public locator; request_id is reserved against replay; an authentication failure returns a uniform 401 without revealing whether the key exists. The secret never travels. The protocol's normative test vector is part of the contract; with it you reproduce the signature byte for byte.
Q08Do I have to send a national ID, email or phone number?
No. The assessment uses opaque person and account refs that only your system resolves. Cleartext PII is not a product requirement. Pointer and keystroke behavior is turned off in the contract. Canvas, WebGL, audio and font checks run under the managed collection policy of every company and leave the browser only as hashes, a GPU family and a presence mask over a fixed font list, never as pixels, sound, GPU names or font names. Password, typed text, clipboard, screenshot, microphone and camera are forbidden. Every kind of evidence has a declared purpose, privacy class and retention; the legal basis is reviewed with your legal team before production is switched on, and nothing on this page replaces that review.
Q09What does it cost and what does my team have to do?
Monthly plans by volume of completed collections, from $59.90 for 20,000. The candidate billing unit is a snapshot completed in production; nothing outside production counts. On your side: one script on the site, one server-to-server endpoint in the backend to ask for the assessment, and two API calls to send back the action taken and the real result (the outcome). The SDK is web and runs in any modern browser; there is no app to install.
Q10What is device intelligence, and how is it different from device fingerprinting?
Device fingerprinting reads stable traits of a browser to recognize it again. Device intelligence starts there and goes further: it checks whether those traits are coherent with each other and with the network, whether automation, an emulator or an anti-detect browser is at work, and how this device behaves across your accounts. FingerTrackID returns it as a trust score, a device match with its own confidence, evidence coverage and 13 indicators, each with the version of the detector that read it.
Q11Can we set our own rules, scores and lists?
Yes. In the console, your company gives points to any of the 13 indicators and 54 context insights, chooses the score bands and the recommendation of each band, and can run any rule as ghost first, computed and recorded without changing the response. Block and allow lists take a device, an account ref or a single IP, one entry at a time, with a note and the name of whoever added it. Every publication is a new audited version. FingerTrackID never activates a rule or adds a list entry for you.
Q12Can an AI coding agent do the integration?
Yes. The server SDK @fingertrackid/server signs the call, the MCP server @fingertrackid/mcp gives Claude Code, Cursor or any MCP-capable agent the contracts, the signature vectors, a ready server module per tag and a check of your collection domain, all read-only, and the documentation publishes llms.txt and a ready block of rules for your AGENTS.md. The secret stays in your environment; the agent never needs to see it.
Q13How heavy is the SDK, and does it slow the page down?
The SDK collects once, in parallel, in a single request to your own collection subdomain. On desktop the full collection took between about 80 and 125 ms at the median in Chrome, Firefox and Safari, under a hard budget of 250 ms; each check takes at most 2 ms of the main thread, and the ones that wait, such as frame timing or a Worker, wait in parallel. It never shows a permission prompt and never blocks your interface.

17 · Contact

Assess the journey you choose, in shadow mode.

Start in shadow mode: the response runs alongside your real flow without touching any decision, you compare, you send back the results, and only then start using the recommendation. If you have screens where the decision matters and you want to see the response explained before trusting it, talk to us.

Book a demo

Enter an email with a domain.
Enter the company name.
What you want to assess

A journey is any screen you choose, with the name you give it. Tick the ones you already have in mind; the rest comes out in the conversation.

No newsletter · we reply by email, one person at a time · or write straight to contato@fingertrackid.com

How it starts

  1. A 30-minute conversation about the journeys you want to assess and your decision flow.
  2. Server credential for your application, executable contract and HMAC vector. There is no sandbox: you test in production, in shadow mode.
  3. Shadow mode with your real flow: you compare and send back results before the response influences any decision.
  4. Production on the screens you tag, with versioned policy; every decision stays with your backend.

What comes with it

  • Console with evidence coverage and, access by access, the recommendation beside the action you reported
  • Executable event-assessment.v1 contract, with an HMAC test vector
  • Shadow mode in production from day one: the response runs beside your flow and never touches a decision
  • Direct channel with the people who write the policy