<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Felicita</title>
    <description>The latest articles on DEV Community by John Felicita (@johnfelicita).</description>
    <link>https://dev.to/johnfelicita</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2017236%2F3a0fe02b-b7f5-4965-99f6-2b87ad88cb0e.png</url>
      <title>DEV Community: John Felicita</title>
      <link>https://dev.to/johnfelicita</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnfelicita"/>
    <language>en</language>
    <item>
      <title>How we automated fraud and metric inconsistency detection on a digital asset marketplace in under 800ms using Next.js App Router and DeepSeek</title>
      <dc:creator>John Felicita</dc:creator>
      <pubDate>Wed, 04 Nov 2026 16:10:59 +0000</pubDate>
      <link>https://dev.to/johnfelicita/how-we-automated-fraud-and-metric-inconsistency-detection-on-a-digital-asset-marketplace-in-under-4ifn</link>
      <guid>https://dev.to/johnfelicita/how-we-automated-fraud-and-metric-inconsistency-detection-on-a-digital-asset-marketplace-in-under-4ifn</guid>
      <description>&lt;p&gt;`&lt;/p&gt;

&lt;p&gt;When building &lt;a href="https://marketplace.lunil.com/en/signin" rel="noopener noreferrer"&gt;Lunil&lt;/a&gt;—a marketplace where founders buy and sell digital assets like SaaS products, websites, domains, and social channels—the hardest architectural problem wasn't escrow integration or database design.&lt;/p&gt;

&lt;p&gt;It was &lt;strong&gt;trust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the digital asset space, fraud is rarely obvious spam like cheap pharma links. It's subtle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A seller claiming $12,000 MRR on a repository that was created 3 weeks ago.&lt;/li&gt;
&lt;li&gt;Asking prices that wildly contradict declared traffic and revenue.&lt;/li&gt;
&lt;li&gt;Subtle phishing links or obfuscated Telegram handles designed to bypass secure escrow payments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initially, every listing had to wait in an admin review queue. For sellers, waiting hours or days killed momentum. For us, manual verification didn't scale.&lt;/p&gt;

&lt;p&gt;We needed a &lt;strong&gt;real-time moderation pipeline&lt;/strong&gt; that evaluates submissions during form completion, gives actionable feedback instantly, and stops deceptive listings before they hit our database.&lt;/p&gt;

&lt;p&gt;Here is how we designed and deployed this system using &lt;strong&gt;Next.js App Router&lt;/strong&gt;, &lt;strong&gt;DeepSeek&lt;/strong&gt;, and &lt;strong&gt;Zod structured outputs&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: Why Asynchronous Wasn't an Option
&lt;/h2&gt;

&lt;p&gt;Most platforms push moderation to background workers (&lt;code&gt;bullmq&lt;/code&gt;, Celery, etc.). But if a seller makes an honest typo in their revenue metric or pastes a broken verification link, discovering it via email 6 hours later creates a terrible user experience.&lt;/p&gt;

&lt;p&gt;We aimed for a &lt;strong&gt;synchronous round-trip under 1.2 seconds&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Pre-flight (&amp;lt; 5ms):&lt;/strong&gt; Fast sanitization, regex rules, link checks, and contact-bypass detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Verification via DeepSeek (~700ms - 900ms):&lt;/strong&gt; Deep evaluation of business metrics, claim consistency, and category alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Scoring &amp;amp; Immediate Feedback:&lt;/strong&gt; Returning a typed JSON payload with discrete flags so the frontend form highlights the exact fields that need fixing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;plaintext&lt;br&gt;
[Seller Submits Listing]&lt;br&gt;
          │&lt;br&gt;
          ▼&lt;br&gt;
┌─────────────────────────┐&lt;br&gt;
│ Next.js Route Handler   │&lt;br&gt;
└──────────┬──────────────┘&lt;br&gt;
           │ 1. Deterministic Sanitization (Regex / Honeypot)&lt;br&gt;
           ▼&lt;br&gt;
┌─────────────────────────┐&lt;br&gt;
│ DeepSeek Structured API │ ◄── Inconsistency &amp;amp; Fraud Detection&lt;br&gt;
└──────────┬──────────────┘&lt;br&gt;
           │ 2. Typed JSON Response (Zod)&lt;br&gt;
           ▼&lt;br&gt;
     ┌───────────┐&lt;br&gt;
     │ Risk Tier │&lt;br&gt;
     └─────┬─────┘&lt;br&gt;
           ├─ Score &amp;lt; 0.25 ──► Instant Publish&lt;br&gt;
           ├─ Score 0.25-0.70 ► Form Warning &amp;amp; Inline Correction&lt;br&gt;
           └─ Score &amp;gt; 0.70 ──► Blocked / Fraud Flag&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Defining the Evaluation Schema
&lt;/h2&gt;

&lt;p&gt;We use &lt;strong&gt;Zod&lt;/strong&gt; to guarantee strict type safety between the LLM output and our Next.js backend. The model is forced to return a JSON object adhering to this schema:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`typescript&lt;br&gt;
// lib/moderation/schema.ts&lt;br&gt;
import { z } from "zod";&lt;/p&gt;

&lt;p&gt;export const ModerationResultSchema = z.object({&lt;br&gt;
  status: z.enum(["APPROVED", "NEEDS_REVISION", "REJECTED"]),&lt;br&gt;
  riskScore: z.number().min(0).max(1), // 0 = legit, 1 = malicious&lt;br&gt;
  issues: z.array(&lt;br&gt;
    z.object({&lt;br&gt;
      field: z.enum(["title", "description", "financials", "urls", "general"]),&lt;br&gt;
      severity: z.enum(["low", "medium", "critical"]),&lt;br&gt;
      message: z.string(),&lt;br&gt;
      suggestion: z.string().optional(),&lt;br&gt;
    })&lt;br&gt;
  ),&lt;br&gt;
  metricConsistency: z.object({&lt;br&gt;
    revenueVsTrafficPlausible: z.boolean(),&lt;br&gt;
    valuationMultipleReasonable: z.boolean(),&lt;br&gt;
    notes: z.string().optional(),&lt;br&gt;
  }),&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;export type ModerationResult = z.infer;&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Next.js Route Handler Implementation
&lt;/h2&gt;

&lt;p&gt;Here is the core verification logic running inside a Next.js App Router API route (&lt;code&gt;app/api/listings/verify/route.ts&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We use &lt;strong&gt;DeepSeek&lt;/strong&gt; (&lt;code&gt;deepseek-chat&lt;/code&gt;) with strict JSON output mode. DeepSeek offers exceptional reasoning on structured tabular data and financial metrics at a fraction of the cost of legacy models.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`typescript&lt;br&gt;
// app/api/listings/verify/route.ts&lt;br&gt;
import { NextResponse } from "next/server";&lt;br&gt;
import { ModerationResultSchema } from "@/lib/moderation/schema";&lt;/p&gt;

&lt;p&gt;const SYSTEM_PROMPT = `&lt;br&gt;
You are a senior due diligence auditor for Lunil, a marketplace for buying and selling digital businesses (SaaS, content sites, domains).&lt;br&gt;
Your task is to analyze listing data for fraud, inflated metrics, and policy violations.&lt;/p&gt;

&lt;p&gt;Evaluate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Metric Coherence: Does the asking price match typical multiples (e.g., 20x-45x monthly net profit)? Does declared revenue align with claimed traffic?&lt;/li&gt;
&lt;li&gt;Circumvention: Detect hidden contact information (Telegram, WhatsApp, email) aimed at evading Escrow protection.&lt;/li&gt;
&lt;li&gt;Deceptive Claims: Unverifiable guarantees (e.g., "Guaranteed $5k/mo passive income with 0 effort").&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Always return valid JSON matching the exact schema requested. Be objective, strict on fraud, but forgiving of normal startup variances.&lt;br&gt;
`;&lt;/p&gt;

&lt;p&gt;export async function POST(req: Request) {&lt;br&gt;
  try {&lt;br&gt;
    const listingData = await req.json();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 1. Quick deterministic heuristic checks
if (!listingData.title || !listingData.description) {
  return NextResponse.json({ error: "Missing required fields" }, { status: 400 });
}

// 2. Query DeepSeek
const response = await fetch("https://api.deepseek.com/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`,
  },
  body: JSON.stringify({
    model: "deepseek-chat",
    response_format: { type: "json_object" },
    messages: [
      { role: "system", content: SYSTEM_PROMPT },
      {
        role: "user",
        content: `Analyze this listing submission:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Title: ${listingData.title}&lt;br&gt;
Asset Type: ${listingData.type}&lt;br&gt;
Asking Price: $${listingData.askingPrice}&lt;br&gt;
Monthly Revenue: $${listingData.monthlyRevenue}&lt;br&gt;
Monthly Profit: $${listingData.monthlyProfit}&lt;br&gt;
Monthly Pageviews: ${listingData.pageviews}&lt;br&gt;
Description:&lt;br&gt;
${listingData.description}&lt;br&gt;
URL / Proof: ${listingData.proofUrl || "None provided"}`,&lt;br&gt;
          },&lt;br&gt;
        ],&lt;br&gt;
        temperature: 0.1, // Near-deterministic for evaluation&lt;br&gt;
      }),&lt;br&gt;
    });&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!response.ok) {
  throw new Error(`DeepSeek API error: ${response.statusText}`);
}

const rawData = await response.json();
const parsedContent = JSON.parse(rawData.choices[0].message.content);

// 3. Validate response structure with Zod
const validationResult = ModerationResultSchema.safeParse(parsedContent);

if (!validationResult.success) {
  console.error("Schema mismatch:", validationResult.error);
  // Fallback: send to manual review queue if AI output is malformed
  return NextResponse.json({ status: "MANUAL_REVIEW" });
}

return NextResponse.json(validationResult.data);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (error) {&lt;br&gt;
    console.error("Listing verification failed:", error);&lt;br&gt;
    return NextResponse.json({ error: "Verification unavailable" }, { status: 500 });&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Real-World Edge Cases We Encountered
&lt;/h2&gt;

&lt;p&gt;Running this in production on &lt;a href="https://lunil.com" rel="noopener noreferrer"&gt;Lunil&lt;/a&gt; highlighted some subtle nuances:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Micro-SaaS Multiple Trap:&lt;/strong&gt; Standard multiples (3x-4x annual profit) don't apply to pre-revenue or code-only projects. A prompt without context flagged valid starter projects selling for $500 as "financially contradictory". We solved this by injecting the asset category (&lt;code&gt;Starter Project&lt;/code&gt; vs &lt;code&gt;Established Cash-Flowing SaaS&lt;/code&gt;) into the system prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Obfuscated Escrow Bypasses:&lt;/strong&gt; Scammers often spell out contacts like &lt;em&gt;“reach me on t . m e / user”&lt;/em&gt; or hide Discord tags inside screenshots. DeepSeek handles natural language obfuscation remarkably well compared to rigid regex patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Correction UX:&lt;/strong&gt; Because the API returns field-specific paths (&lt;code&gt;field: "financials"&lt;/code&gt;), the client UI immediately highlights the exact input in red with the AI’s explanation (e.g., &lt;em&gt;"Claimed monthly profit ($3,000) exceeds declared revenue ($2,500)"&lt;/em&gt;). Sellers can fix legitimate typos without opening a support ticket.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What’s Next: Exploring "System 1" AI Models (Jev by TypeSafe AI)
&lt;/h2&gt;

&lt;p&gt;While DeepSeek handles deep reasoning well in ~800ms, our long-term goal is to make listing moderation feel as instantaneous as form validation (&lt;code&gt;&amp;lt; 150ms&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Generative LLMs are fundamentally &lt;strong&gt;System 2&lt;/strong&gt; thinkers: they predict tokens sequentially, consuming unnecessary computational overhead when all we truly need is a deterministic categorical status and risk score.&lt;/p&gt;

&lt;p&gt;Through December, we are benchmarking &lt;strong&gt;Jev&lt;/strong&gt;, a high-speed "System 1" model developed by &lt;strong&gt;TypeSafe AI&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Because System 1 architectures focus on fast, direct pattern recognition without multi-step autoregressive generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response times drop toward low dual-digit milliseconds.&lt;/li&gt;
&lt;li&gt;Output formats are inherently type-safe by design.&lt;/li&gt;
&lt;li&gt;Infrastructure costs scale down dramatically for high-throughput marketplace operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the benchmark proves that Jev matches DeepSeek’s accuracy on financial anomaly detection, our production pipeline will evolve into a two-tier engine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Jev (System 1):&lt;/strong&gt; Real-time gatekeeper evaluating submissions in &amp;lt;100ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek (System 2):&lt;/strong&gt; Fallback reasoning engine invoked only when complex due diligence and textual synthesis are required.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What’s the Verdict?
&lt;/h2&gt;

&lt;p&gt;Automating trust on a digital asset marketplace doesn't require multi-million dollar ML budgets. A disciplined setup using &lt;strong&gt;Next.js App Router&lt;/strong&gt;, &lt;strong&gt;Zod&lt;/strong&gt;, and &lt;strong&gt;DeepSeek&lt;/strong&gt; allows a lean team to deliver real-time fraud protection that protects buyers while keeping the seller onboarding frictionless.&lt;/p&gt;

&lt;p&gt;If you are building marketplaces or UGC platforms, how are you handling metric validation and fraud detection? Are you sticking to deterministic queues, or moving toward hybrid real-time architectures?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Next.js, DeepSeek, and deployed on &lt;a href="https://lunil.com" rel="noopener noreferrer"&gt;Lunil&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>deepseek</category>
      <category>jev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
