JavaScript / TypeScript
Requirements
Section titled “Requirements”Node.js 18+ includes fetch and AbortSignal.timeout natively — no additional dependencies required.
For older Node.js versions:
npm install node-fetchBasic Example
Section titled “Basic Example”async function verifySubmission(formData) { const response = await fetch('https://api.formsentry.ai/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.FORMSENTRY_API_KEY, formId: process.env.FORMSENTRY_FORM_ID, payload: formData }), signal: AbortSignal.timeout(5000) });
if (!response.ok) { throw new Error(`FormSentry error: ${response.status}`); }
return response.json();}
// UsageverifySubmission({ name: 'John Doe', email: 'john@example.com', message: 'I would like to learn more about your services.'}) .then(result => { if (result.status === 'spam') { console.log('Spam detected:', result.reasoning); } else { console.log('Legitimate submission'); } }) .catch(console.error);Express Integration
Section titled “Express Integration”import express from 'express';
const app = express();app.use(express.json());
app.post('/contact', async (req, res) => { const { name, email, message } = req.body;
if (!name || !email || !message) { return res.status(400).json({ error: 'Missing required fields' }); }
let result; try { const verification = await fetch('https://api.formsentry.ai/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.FORMSENTRY_API_KEY, formId: process.env.FORMSENTRY_FORM_ID, payload: { name, email, message } }), signal: AbortSignal.timeout(5000) });
if (!verification.ok) throw new Error(`HTTP ${verification.status}`); result = await verification.json(); } catch (error) { // Fail open — if FormSentry is unreachable, allow the submission console.error('FormSentry error:', error.message); result = { status: 'legitimate', confidence: 0.0 }; }
if (result.status === 'spam') { // Silently accept to avoid tipping off spammers return res.json({ success: true }); }
// TODO: Process the legitimate submission // e.g., save to database, send email notification res.json({ success: true });});
app.listen(3000);Fastify Integration
Section titled “Fastify Integration”import Fastify from 'fastify';
const app = Fastify();
app.post('/contact', async (request, reply) => { const { name, email, message } = request.body ?? {};
if (!name || !email || !message) { return reply.status(400).send({ error: 'Missing required fields' }); }
let result; try { const verification = await fetch('https://api.formsentry.ai/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.FORMSENTRY_API_KEY, formId: process.env.FORMSENTRY_FORM_ID, payload: { name, email, message } }), signal: AbortSignal.timeout(5000) });
if (!verification.ok) throw new Error(`HTTP ${verification.status}`); result = await verification.json(); } catch (error) { // Fail open — if FormSentry is unreachable, allow the submission console.error('FormSentry error:', error.message); result = { status: 'legitimate', confidence: 0.0 }; }
if (result.status === 'spam') { return reply.send({ success: true }); }
// TODO: Process legitimate submission reply.send({ success: true });});
app.listen({ port: 3000 });Next.js API Route
Section titled “Next.js API Route”import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) { let formData; try { formData = await request.json(); } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }); }
if (!formData?.name || !formData?.email || !formData?.message) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); }
let result; try { const verification = await fetch('https://api.formsentry.ai/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.FORMSENTRY_API_KEY, formId: process.env.FORMSENTRY_FORM_ID, payload: formData }), signal: AbortSignal.timeout(5000) });
if (!verification.ok) throw new Error(`HTTP ${verification.status}`); result = await verification.json(); } catch (error) { // Fail open — if FormSentry is unreachable, allow the submission console.error('FormSentry error:', error); result = { status: 'legitimate', confidence: 0.0 }; }
if (result.status === 'spam') { return NextResponse.json({ success: true }); }
// TODO: Process legitimate submission return NextResponse.json({ success: true });}TypeScript Types
Section titled “TypeScript Types”interface FormSentryRequest { apiKey: string; formId: string; payload: Record<string, unknown>;}
interface FormSentryResponse { status: 'legitimate' | 'spam'; confidence: number; reasoning: string; submissionId: string; processingTime: number; formId?: string;}
async function verify(payload: Record<string, unknown>): Promise<FormSentryResponse> { const response = await fetch('https://api.formsentry.ai/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.FORMSENTRY_API_KEY, formId: process.env.FORMSENTRY_FORM_ID, payload } satisfies FormSentryRequest), signal: AbortSignal.timeout(5000) });
if (!response.ok) { throw new Error(`FormSentry error: ${response.status}`); }
return response.json() as Promise<FormSentryResponse>;}Error Handling
Section titled “Error Handling”async function verifyWithRetry(payload, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const response = await fetch('https://api.formsentry.ai/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.FORMSENTRY_API_KEY, formId: process.env.FORMSENTRY_FORM_ID, payload }), signal: AbortSignal.timeout(5000) });
if (response.ok) return response.json();
if (response.status === 429 || response.status >= 500) { const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); await new Promise(resolve => setTimeout(resolve, delay)); continue; }
throw new Error(`FormSentry error: ${response.status}`); } catch (error) { if (attempt === maxRetries) throw error; } }}See Errors for all error codes and retry strategies.