# New Account Fraud Prevention

### The Problem

Every business with a signup flow faces the same challenge: fraudsters create multiple fake accounts to abuse your system. Whether it's claiming free trials repeatedly, gaming referral programs, or building networks for spam and scam operations, fake account creation costs businesses billions annually.

Traditional defences fail because:

* **Email verification** is trivial to bypass with disposable email services
* **Phone verification** can be defeated with virtual phone numbers
* **IP blocking** fails against VPNs and rotating proxies
* **CAPTCHA** is easily solved by bot farms and AI services
* **Rate limiting by IP** misses distributed attacks

{% hint style="danger" %}
**The result:** A single fraudster can create hundreds of accounts, each appearing legitimate in isolation.
{% endhint %}

#### Common Attack Vectors

| Attack Type               | Description                                                 | Business Impact              |
| ------------------------- | ----------------------------------------------------------- | ---------------------------- |
| **Free Trial Abuse**      | Creating new accounts to extend free trials indefinitely    | Lost revenue, skewed metrics |
| **Promo/Coupon Stacking** | Using multiple accounts to claim one-time offers repeatedly | Direct financial loss        |
| **Referral Fraud**        | Self-referring between fake accounts to earn bonuses        | Inflated CAC, program abuse  |
| **Review Manipulation**   | Fake accounts posting fraudulent reviews                    | Damaged trust, legal risk    |
| **Bonus Abuse**           | Exploiting sign-up bonuses across multiple accounts         | Direct financial loss        |
| **Content Spam**          | Automated account creation for spam distribution            | Platform degradation         |

### The Solution: Device-Based Identity

Guardian Stack links accounts to **devices, not just credentials**. Even when a fraudster uses a new email, phone number, VPN, and incognito mode, they're still using the same physical device — and Guardian detects it.

#### How It Works

<div data-with-frame="true"><figure><img src="/files/BfKSspuHPMoDowUtVwJh" alt=""><figcaption></figcaption></figure></div>

1. User visits signup page
2. Guardian SDK silently collects device signals
3. User submits registration form
4. Your backend fetches the Guardian event and checks:
   1. Is this device linked to existing accounts?
   2. Is this a bot or automated browser?
   3. Is the user hiding behind a VPN/proxy?
5. Allow, challenge, or deny registration

{% hint style="info" %}
**Key insight:** The `visitorId` persists across incognito sessions, cleared cookies, and browser restarts. It's cryptographically tied to the physical device.
{% endhint %}

***

### Implementation Guide

#### Step 1: Frontend — Capture Device Signals

Install the Guardian JS SDK:

```bash
npm install @guardianstack/guardian-js
```

Initialize Guardian when your app loads, then call `.get()` during registration:

```typescript
import { loadAgent } from "@guardianstack/guardian-js";

// Initialize once when your app starts
const guardian = await loadAgent({
  siteKey: "YOUR_SITE_KEY",
});

async function handleSignup(formData: SignupFormData) {
  // 1. Get Guardian signals before submitting
  const response = await guardian.get();
  const requestId = response?.requestId;

  // 2. Submit registration with Guardian requestId
  const result = await fetch("/api/signup", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      ...formData,
      guardianRequestId: requestId,
    }),
  });

  return result.json();
}
```

#### Step 2: Backend — Verify & Decide

Install the Guardian Server SDK:

```bash
npm install @guardianstack/guardianjs-server
```

Create your signup endpoint with fraud checks:

```typescript
import {
  createGuardianClient,
  isBot,
  isVPN,
  isTampering,
  isIncognito,
  isVirtualized,
} from "@guardianstack/guardianjs-server";

const guardianClient = createGuardianClient({
  secret: process.env.GUARDIAN_SECRET_KEY!,
});

app.post("/api/signup", async (req, res) => {
  const { email, password, guardianRequestId } = req.body;

  // 1. Fetch the Guardian event
  const event = await guardianClient.getEvent(guardianRequestId);

  // 2. Extract key identifiers
  const visitorId = event.identification.visitorId;
  const ipAddress = event.identification.ip;

  // 3. Check for automation/bot signals
  if (isBot(event)) {
    return res.status(403).json({
      error: "Registration blocked",
      reason: "Automated access detected",
    });
  }

  // 4. Check for browser tampering (anti-detect browsers)
  if (isTampering(event)) {
    return res.status(403).json({
      error: "Registration blocked",
      reason: "Browser integrity check failed",
    });
  }

  // 5. Check if this device has created accounts before
  const existingAccounts = await db.accounts.findMany({
    where: { visitorId },
  });

  if (existingAccounts.length > 0) {
    return res.status(403).json({
      error: "Registration blocked",
      reason: "You've already created an account",
    });
  }

  // 6. Check velocity (too many attempts from this device)
  const velocity = event.velocity;
  if (velocity["24h"] > 10) {
    return res.status(429).json({
      error: "Too many attempts",
      reason: "Please try again later",
    });
  }

  // 7. All checks passed — create the account
  const newUser = await db.accounts.create({
    data: {
      email,
      password: await hashPassword(password),
      visitorId, // Store for future fraud detection
      signupIp: ipAddress,
      createdAt: new Date(),
    },
  });

  return res.status(201).json({ success: true, userId: newUser.id });
});
```

***

### Real-World Examples

#### Free Trial Abuse Prevention

**Scenario:** Your SaaS offers a 15-day free trial. Fraudsters create new accounts every 15 days to avoid paying.

```typescript
app.post("/api/start-trial", async (req, res) => {
  const { guardianRequestId, email } = req.body;

  const event = await guardianClient.getEvent(guardianRequestId);
  const visitorId = event.identification.visitorId;

  // Check if this device has EVER started a trial
  const previousTrials = await db.trials.findMany({
    where: { visitorId },
  });

  if (previousTrials.length > 0) {
    const lastTrial = previousTrials[0];
    return res.status(403).json({
      error: "Trial unavailable",
      message: "You've already used a free trial on this device",
      previousEmail: maskEmail(lastTrial.email), // "j***@example.com"
    });
  }

  // Create the trial and link to device
  await db.trials.create({
    data: {
      email,
      visitorId,
      startedAt: new Date(),
      expiresAt: addDays(new Date(), 15),
    },
  });

  return res.json({ success: true, trialDays: 15 });
});
```

#### Referral Program Protection

**Scenario:** You offer $20 for each referred user. Fraudsters refer themselves using multiple accounts.

```typescript
app.post("/api/apply-referral", async (req, res) => {
  const { referralCode, guardianRequestId } = req.body;

  const event = await guardianClient.getEvent(guardianRequestId);
  const newUserVisitorId = event.identification.visitorId;

  // Find the referrer
  const referrer = await db.users.findUnique({
    where: { referralCode },
  });

  if (!referrer) {
    return res.status(404).json({ error: "Invalid referral code" });
  }

  // Check if referrer and new user share the same device
  if (referrer.visitorId === newUserVisitorId) {
    // Log for fraud review but don't reveal detection
    await logFraudAttempt("self_referral", {
      referrerId: referrer.id,
      visitorId: newUserVisitorId,
    });

    return res.status(400).json({
      error: "Referral not applied",
      message: "This referral code cannot be used",
    });
  }

  // Check if this device has been referred before
  const previousReferrals = await db.referrals.findMany({
    where: { referredVisitorId: newUserVisitorId },
  });

  if (previousReferrals.length > 0) {
    return res.status(400).json({
      error: "Referral not applied",
      message: "This device has already been referred",
    });
  }

  // Valid referral — credit the referrer
  await db.referrals.create({
    data: {
      referrerId: referrer.id,
      referredVisitorId: newUserVisitorId,
      bonusAmount: 20,
      status: "pending",
    },
  });

  return res.json({ success: true, message: "Referral bonus applied!" });
});
```

#### Coupon/Promo Code Abuse Prevention

**Scenario:** A one-time 50% discount code is being reused across fake accounts.

```typescript
app.post("/api/apply-promo", async (req, res) => {
  const { promoCode, guardianRequestId } = req.body;

  const event = await guardianClient.getEvent(guardianRequestId);
  const visitorId = event.identification.visitorId;

  const promo = await db.promoCodes.findUnique({
    where: { code: promoCode },
  });

  if (!promo || !promo.isActive) {
    return res.status(404).json({ error: "Invalid promo code" });
  }

  // Check if promo is one-per-device
  if (promo.onePerDevice) {
    const usedBefore = await db.promoUsage.findFirst({
      where: {
        promoId: promo.id,
        visitorId,
      },
    });

    if (usedBefore) {
      return res.status(400).json({
        error: "Promo code already used",
        message: "This offer is limited to one per device",
      });
    }
  }

  // Record usage and apply discount
  await db.promoUsage.create({
    data: {
      promoId: promo.id,
      visitorId,
      usedAt: new Date(),
    },
  });

  return res.json({
    success: true,
    discount: promo.discountPercent,
  });
});
```

***

### Database Schema Example

Store Guardian identifiers for long-term fraud detection:

```sql
-- Users table with fraud prevention fields
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,

  -- Guardian fraud prevention
  visitor_id VARCHAR(255),          -- Persistent device ID
  signup_ip INET,                   -- IP at registration
  signup_risk_score INTEGER,        -- Trust score at signup

  -- Metadata
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Index for quick device lookups
CREATE INDEX idx_users_visitor_id ON users(visitor_id);

-- Track all visitor IDs seen for a user (device history)
CREATE TABLE user_devices (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id),
  visitor_id VARCHAR(255) NOT NULL,
  first_seen_at TIMESTAMP DEFAULT NOW(),
  last_seen_at TIMESTAMP DEFAULT NOW(),
  is_trusted BOOLEAN DEFAULT false,

  UNIQUE(user_id, visitor_id)
);

-- Fraud events for review
CREATE TABLE fraud_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  event_type VARCHAR(50) NOT NULL,  -- 'duplicate_device', 'bot_detected', etc.
  visitor_id VARCHAR(255),
  ip_address INET,
  request_data JSONB,
  guardian_event JSONB,             -- Store full event for analysis
  created_at TIMESTAMP DEFAULT NOW()
);
```

***

### Best Practices

#### Do

* **Store `visitorId`** with every account for future cross-referencing
* **Log fraud attempts** without revealing detection methods to users
* **Use risk scoring** for graduated responses instead of hard blocks
* **Combine signals** — a VPN alone isn't fraud, but VPN + incognito + high velocity is suspicious
* **Review edge cases** — legitimate users sometimes trigger signals

#### Don't

* **Don't block VPN users outright** — many legitimate users use VPNs for privacy
* **Don't reveal detection methods** in error messages (avoid "Bot detected")
* **Don't rely solely on IP** — it's easily changed
* **Don't ignore velocity** — rapid signups from one device indicate automation

***

### Testing Your Implementation

Use these scenarios to verify your fraud detection:

| Test Case                        | Expected Behavior                     |
| -------------------------------- | ------------------------------------- |
| Normal signup                    | Account created successfully          |
| Same device, new email           | Blocked ("Already have an account")   |
| Incognito mode signup            | Slight score reduction, still allowed |
| VPN + incognito + rapid attempts | Challenge or block                    |
| Automated browser (Puppeteer)    | Blocked (bot detected)                |
| Anti-detect browser              | Blocked (tampering detected)          |

***

### Conclusion

New account fraud is a persistent threat that traditional verification methods can't solve. By linking accounts to physical devices through Guardian Stack's `visitorId`, you can:

* **Stop serial abusers** who create multiple accounts
* **Protect promotional offers** from exploitation
* **Preserve referral program integrity**
* **Reduce manual fraud review** with automated detection
* **Maintain good user experience** for legitimate customers

The key insight: fraudsters can change emails, phone numbers, and IP addresses easily — but they can't easily change their physical device. Guardian Stack makes device identity the foundation of your fraud prevention strategy.

***

{% hint style="success" %}
**Get Started:** [Sign up for Guardian Stack](https://dashboard.guardianstack.ai/) and get your API keys to implement this today.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.guardianstack.ai/documentation/protect-your-implementation/new-account-fraud-prevention.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
