Gineadh an t-aistriúchán seo le meaisín agus tá sé ag feitheamh ar athbhreithniú.Athraigh go Béarla
Dorcha
DeaisDéan teagmháil
Ar an leathanach seo

Backend Integration

Use the backend integration when a signup, login, checkout, account change, or API request needs a trusted decision. The browser supplies a fingerprintId; your server uses an authenticated API lookup to retrieve the risk record and combines it with your own business context.

Do not make a high-impact decision from a browser-visible score alone. Browser responses can intentionally suppress numeric verdict fields, and anything in browser code can be altered by the visitor.

Overview

The supported lookup flow is:

  1. Browser: collect with the script tag or @noxtica/sdk.
  2. Browser: read the returned fingerprintId, whether the browser result is scored or suppressed.
  3. Your application: submit that identifier with the business action to your own backend.
  4. Backend: call GET /v1/device/:deviceId with a secret API key scoped to read:device.
  5. Backend: distinguish found, missing, unauthorized, rate-limited, and unavailable outcomes.
  6. Policy: start by logging the proposed action; add step-up or block behavior only after reviewing results against your own outcomes.

Step 1: Embed the Collector

Place the optional session-replay head helper first in <head>, then load the collector asynchronously. data-auto-check-once is required here so auto-init actually requests a cache-respecting assessment.

<script
	src="https://collect.noxtica.com/collector/noxtica-recorder-head.d9ae068e.js"
	integrity="sha384-0aarRZ9aHZq4csV3R+MzJUTnerNzrddznWNV/KoKPkKp2Qh40kGsrFiPiMAuvmg9"
	crossorigin="anonymous"
></script>
<script
	src="https://collect.noxtica.com/collector/noxtica.js"
	data-site-key="pk_prod_your_site_key_here"
	data-auto-init
	data-auto-check-once
	async
></script>

The first script is needed only for early session-replay coverage. It does not transmit by itself, and replay remains subject to eligibility, policy, browser privacy signals, sampling, and consent or other approved basis. See Getting Started for strict CSP, SRI upgrades, tag managers, and consent details.

Or collect manually:

const client = NoxticaCollector.createClient({
	siteKey: 'pk_prod_your_site_key_here',
});

const result = await client.checkOnce();
console.log('Fingerprint ID:', result.fingerprintId);

if (typeof result.score === 'number' && result.risk_level) {
	console.log('Browser-visible risk:', result.risk_level, result.score);
} else {
	console.log('Browser verdict suppressed:', result.status);
}

A suppressed browser verdict can still provide the fingerprintId needed for an authenticated backend lookup. A thrown error or missing identifier is different: no usable fresh identifier was returned.

Step 2: Store the Fingerprint ID

Send the identifier to your backend with the action it will inform. Do not put it in a URL.

const result = await client.checkOnce();

if (!result.fingerprintId) {
	throw new Error('No fingerprint ID is available for server lookup');
}

await fetch('/api/login', {
	method: 'POST',
	headers: { 'Content-Type': 'application/json' },
	body: JSON.stringify({
		email: userEmail,
		password: userPassword,
		deviceId: result.fingerprintId,
	}),
});

On your backend, associate deviceId with the current request or authenticated session only after applying your normal authentication and input validation:

app.post('/api/login', async (req, res) => {
	const { email, password, deviceId } = req.body;
	const user = await authenticate(email, password);

	if (!user) {
		return res.status(401).json({ error: 'invalid_credentials' });
	}

	req.session.deviceId = deviceId;
	return res.json({ ok: true });
});

A device identifier is not proof of identity. Do not use it as an authentication token or permanent person identifier.

Step 3: Create a Server API Key

In Backoffice:

  1. Open API Config.
  2. Create a key for the backend service that will perform lookups.
  3. Grant only read:device for the endpoint in this guide.
  4. Save the secret when it is shown and place it in your server-side secret manager.

Server API keys use an sk_... form. Never embed one in browser JavaScript, HTML, mobile application resources, URLs, or client-visible logs. Rotate or revoke a key according to your operational policy when its ownership changes or exposure is suspected.

Step 4: Look Up Device Data

The lookup endpoint is GET /v1/device/:deviceId:

const NOXTICA_API = 'https://collect.noxtica.com';
const SECRET_KEY = process.env.NOXTICA_API_KEY;

async function lookupDevice(deviceId) {
	const response = await fetch(`${NOXTICA_API}/v1/device/${encodeURIComponent(deviceId)}`, {
		headers: {
			Authorization: `Bearer ${SECRET_KEY}`,
		},
	});

	if (response.status === 404) {
		// New, expired, deleted, or not present in this tenant.
		return null;
	}

	if (response.status === 401) {
		throw new Error('Noxtica API key is missing or invalid');
	}

	if (response.status === 403) {
		throw new Error('Noxtica API key lacks read:device');
	}

	if (response.status === 429) {
		const error = new Error('Noxtica device lookup rate limited');
		error.retryAfter = response.headers.get('Retry-After');
		throw error;
	}

	if (!response.ok) {
		throw new Error(`Noxtica device lookup unavailable: ${response.status}`);
	}

	return response.json();
}

Example scored response:

{
	"ok": true,
	"device": {
		"deviceId": "abc123...",
		"domainId": "d_xyz...",
		"score": 25,
		"riskLevel": "low",
		"confidence": 0.6,
		"flags": [],
		"reasonCodes": [],
		"country": "US",
		"city": "San Francisco",
		"createdAt": "2025-01-01T00:00:00.000Z",
		"lastSubmittedAt": "2025-01-05T12:00:00.000Z",
		"lastSeenAt": "2025-01-07T08:30:00.000Z"
	}
}

Some additive fields depend on record age, tenant eligibility, and the assessment that ran. Write consumers to tolerate null, empty, and additional fields rather than treating absence as a clean result.

Step 5: Use the Data

Keep transport state separate from risk. The following example proposes an application action without turning a missing or failed lookup into low:

async function proposeTransactionAction(deviceId) {
	let response;

	try {
		response = await lookupDevice(deviceId);
	} catch (error) {
		return {
			action: 'step_up',
			reason: 'risk_lookup_unavailable',
			error,
		};
	}

	if (!response?.device) {
		return {
			action: 'step_up',
			reason: 'device_unknown',
		};
	}

	const { riskLevel, score, confidence } = response.device;
	if (typeof score !== 'number' || typeof confidence !== 'number' || !riskLevel) {
		return {
			action: 'review',
			reason: 'risk_record_incomplete',
		};
	}

	if (riskLevel === 'critical' || riskLevel === 'high') {
		return {
			action: 'step_up',
			reason: 'elevated_device_risk',
			score,
			confidence,
		};
	}

	if (riskLevel === 'medium') {
		return {
			action: 'observe',
			reason: 'review_device_risk',
			score,
			confidence,
		};
	}

	return {
		action: 'allow',
		reason: 'limited_device_risk_evidence',
		score,
		confidence,
	};
}

This is a policy shape, not a universal threshold recommendation. Pair the device result with your own account age, authentication state, transaction value, abuse history, and protected operation. Start with observe, capture the proposed action, and compare it with known outcomes before introducing friction.

API Reference

GET /v1/device/:deviceId

Looks up one device record within the API key’s tenant.

Authentication: Authorization: Bearer sk_...

Required scope: read:device

Response fields:

FieldTypeCustomer-visible meaning
deviceIdstringDevice assessment identifier.
domainIdstringDomain associated with the record.
scorenumberRisk score from 0 to 100 when the stored record is scored.
riskLevelstringminimal, low, medium, high, or critical.
confidencenumberConfidence attached to this assessment.
flagsstring[]Detailed server-visible risk flags, when present.
reasonCodesstring[]Published reason codes, when available for the tenant and record.
subscoresobject or nullCategory-level score information, when available.
detailsobject or nullAdditional server-visible explanation, when available.
countrystring or nullCountry context associated with the last assessment.
citystring or nullCity context associated with the last assessment.
createdAtstringFirst-seen timestamp.
updatedAtstringLast record-update timestamp.
lastSubmittedAtstringLast full assessment timestamp.
lastSeenAtstringLast observed timestamp.

Do not write policy against undocumented fields or assume optional explanation fields are always populated.

Error responses:

StatusMeaningApplication handling
401Missing or invalid API key.Fix credentials; do not retry as low risk.
403Key lacks read:device.Fix the key scope.
404No current record in this tenant.Treat as unknown; choose your new-device policy.
429Read limit exceeded.Honor Retry-After; use your unavailable policy.
5xxService or configuration error.Apply your unavailable policy and retain request context for troubleshooting.

Risk Levels Reference

Score rangeLevelSuggested evaluation posture
0–19minimalAllow or observe, subject to your other controls.
20–39lowAllow or observe; limited risk evidence is not identity proof.
40–59mediumObserve or review before adding friction.
60–79highConsider step-up when confidence and business context support it.
80–100criticalConsider the strongest reviewed response for that surface.

These bands describe a scored record only. A missing record, request failure, malformed response, or suppressed browser verdict has no numeric score and must not be placed in the minimal or low band.

Best Practices

Store deviceId securely

Store a fingerprintId only where the decision workflow needs it:

  • in your server-side session;
  • in a tenant-scoped database record linked to the relevant operation; or
  • in a protected audit event with an appropriate retention period.

Do not expose it in URLs, use it as a login credential, or send it to unrelated analytics systems.

Handle missing devices gracefully

A 404 can represent a new, expired, deleted, or tenant-mismatched record. Model that as unknown, not low:

const result = await lookupDevice(deviceId);

if (!result) {
	return {
		riskState: 'unknown',
		action: 'step_up',
		reason: 'device_not_found',
	};
}

Choose the fallback per operation. A read-only page, a password reset, and a high-value transaction may need different responses.

Respect rate limits

Do not hard-code an assumed limit. On 429, honor the Retry-After header, stop tight retry loops, and record enough request context to investigate volume. Authentication and scope errors require configuration changes, not retries.

Cache lookup results

Short-lived application caching can reduce repeated reads within one decision window. Scope cache entries by the device identifier and your own tenant or environment boundary, cap the cache, and never extend the risk record beyond the freshness your policy accepts.

const cache = new Map();
const CACHE_TTL_MS = 60_000;

async function getCachedDevice(deviceId) {
	const cached = cache.get(deviceId);
	if (cached && Date.now() < cached.expiresAt) {
		return cached.value;
	}

	const value = await lookupDevice(deviceId);
	if (value) {
		cache.set(deviceId, {
			value,
			expiresAt: Date.now() + CACHE_TTL_MS,
		});
	}
	return value;
}

Use a bounded production cache rather than an ever-growing process Map; the example shows the freshness rule only.

Other API Endpoints

The Server API also exposes operational views. Grant their distinct scopes only to services that need them:

EndpointMethodScopeCustomer task
/v1/devicesGETread:fingerprintsList and filter device records.
/v1/eventsGETread:eventsReview submission events.
/v1/visitsGETread:visitsReview visit activity.
/v1/exports/fingerprintsGETread:exportExport device data for governed analysis.

Use the Features Reference to understand the corresponding operator workflows. For policy behavior, see Browser Security and run it in observation mode first.

Next Steps