Quick examples
Copy-paste Cipher recipes for common needs. Grab one, adjust, ship.
Short, self-contained snippets for common needs. Each one assumes you have a Cipher client configured:
import { Cipher } from '@surbee/cipher';
const cipher = new Cipher({ apiKey: process.env.CIPHER_API_KEY!, tier: 4 });See the SDK guide for the full walkthrough. This page grows over time, so check back for new recipes.
React: a form that captures behavior and submits
import { useEffect, useRef } from 'react';
import { startTracker } from './useBehaviorTracker';
export function SurveyForm() {
const tracker = useRef<ReturnType<typeof startTracker>>();
useEffect(() => { tracker.current = startTracker(); }, []);
async function onSubmit(answers: { question: string; answer: string }[]) {
const { metrics, device } = tracker.current!.stop();
const res = await fetch('/api/survey/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ answers, metrics, device }),
});
if (!res.ok) showMessage('We could not verify this submission.');
}
// ... render your fields, call onSubmit(answers) on submit
}Next.js: validate on submit and gate
// app/api/survey/submit/route.ts
import { Cipher } from '@surbee/cipher';
import { NextResponse } from 'next/server';
const cipher = new Cipher({ apiKey: process.env.CIPHER_API_KEY!, tier: 4 });
export async function POST(req: Request) {
const { answers, metrics, device } = await req.json();
const result = await cipher.validate({ responses: answers, behavioralMetrics: metrics, deviceInfo: device });
if (result.recommendation === 'discard') {
return NextResponse.json({ ok: false, reason: result.summary.verdict }, { status: 422 });
}
await saveResponse({ answers, cipher: { score: result.score, recommendation: result.recommendation, flags: result.flags } });
return NextResponse.json({ ok: true });
}Express: a validation endpoint
import express from 'express';
import { Cipher } from '@surbee/cipher';
const cipher = new Cipher({ apiKey: process.env.CIPHER_API_KEY!, tier: 3 });
const app = express();
app.use(express.json());
app.post('/validate', async (req, res) => {
try {
const result = await cipher.validate(req.body);
res.json({ recommendation: result.recommendation, score: result.score, flags: result.flags });
} catch (err: any) {
res.status(err.code === 'INSUFFICIENT_CREDITS' ? 402 : 500).json({ error: err.message });
}
});Screen an exported dataset (CSV or JSON)
const rows = loadExport(); // your respondents
const batch = await cipher.validateBatch({
submissions: rows.map(toValidationInput),
crossAnalysis: true, // catch duplicate answers and shared devices across the set
});
const clean = rows.filter((_, i) => batch.results[i].recommendation === 'keep');
const flagged = rows.filter((_, i) => batch.results[i].recommendation !== 'keep');A full version with field mapping and report output ships with the SDK at
examples/researcher-walkthrough.ts.
Route reviews to a queue
switch (result.recommendation) {
case 'keep': await accept(response); break;
case 'review': await sendToReviewQueue(response, result.flags); break;
case 'discard': await quarantine(response, result.summary); break;
}Retry safely on transient errors
async function validateWithRetry(input: ValidationInput, tries = 3) {
for (let i = 0; i < tries; i++) {
try {
return await cipher.validate(input);
} catch (err: any) {
// Do not retry auth, credit, or tier errors. Only network/server ones.
if (['INVALID_API_KEY', 'INSUFFICIENT_CREDITS', 'TIER_NOT_AVAILABLE'].includes(err.code)) throw err;
if (i === tries - 1) throw err;
await new Promise((r) => setTimeout(r, 250 * 2 ** i));
}
}
}Capture behavior in the browser
A minimal tracker that collects the signals the checks read. No extra library needed.
// useBehaviorTracker.ts
export function startTracker() {
const startedAt = Date.now();
let keypressCount = 0, backspaceCount = 0, pasteEvents = 0, tabSwitchCount = 0;
const onKey = (e: KeyboardEvent) => { keypressCount++; if (e.key === 'Backspace') backspaceCount++; };
const onPaste = () => { pasteEvents++; };
const onBlur = () => { tabSwitchCount++; };
document.addEventListener('keydown', onKey);
document.addEventListener('paste', onPaste);
window.addEventListener('blur', onBlur);
return {
stop() {
document.removeEventListener('keydown', onKey);
document.removeEventListener('paste', onPaste);
window.removeEventListener('blur', onBlur);
return {
metrics: { sessionId: crypto.randomUUID(), duration: Date.now() - startedAt, keypressCount, backspaceCount, pasteEvents, tabSwitchCount },
device: {
userAgent: navigator.userAgent, platform: navigator.platform, language: navigator.language,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
screenWidth: screen.width, screenHeight: screen.height, webDriver: navigator.webdriver === true,
},
};
},
};
}