Use test keys (sk_test_…) in the playground. Test traffic never bills. Live keys need a licensed site.
AbuseGraphDocs
Guides

AbuseGraph + Better Auth

Check Better Auth signup and login with the HTTPS API and optional browser signals.

Quick path

  1. Copy a test key from the console; no package installation is required for the browser or HTTPS API path.
  2. Collect browser signals on signup/login forms → toCheckBody → send to your server.
  3. Use databaseHooks before hooks to gate account creation and session issuance.

Keys

KeyEnvUse
SecretABUSEGRAPH_SECRET_KEY (sk_test_… / sk_live_…)Server /api/v1/check
PublishableNEXT_PUBLIC_ABUSEGRAPH_PUBLISHABLE_KEY (pk_test_…)Browser SDK
SiteABUSEGRAPH_SITEDNS-verified domain

Free includes 1,000 live API checks / month after DNS verify. Test keys never bill.

Env

ABUSEGRAPH_SITE=yourdomain.com
ABUSEGRAPH_CHECK_URL=https://abusegraph.com/api/v1/check
ABUSEGRAPH_SECRET_KEY=sk_test_…
ABUSEGRAPH_SECRET_KEY_TEST=sk_test_…
NEXT_PUBLIC_ABUSEGRAPH_PUBLISHABLE_KEY=pk_test_…

Use sk_live_… / pk_live_… in production after DNS verify.

Server check

Call the HTTPS API from your server. Keep ABUSEGRAPH_SECRET_KEY in server-only environment configuration. Events: signup | login | password_reset | email_change; sessionId is optional.

const API = "https://abusegraph.com/api/v1/check";

export async function scoreAuth(input: Record<string, unknown>) {
  const site = process.env.ABUSEGRAPH_SITE!;
  const response = await fetch(API, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": process.env.ABUSEGRAPH_SECRET_KEY!,
      "x-abusegraph-site": site,
    },
    body: JSON.stringify({
      ...input,
      event: typeof input.event === "string" ? input.event : "signup",
      site,
    }),
  });
  if (!response.ok) throw new Error(`AbuseGraph check failed: ${response.status}`);
  return response.json();
}

Run this before creating an account or issuing a session. Handle block and challenge according to your policy; 402 means the live monthly allowance is exhausted.

Load the browser SDK from the AbuseGraph CDN before your signup/login code. The publishable key is safe to expose; send the resulting body to your own backend, which makes the authoritative check with its server-held secret key.

<script src="https://abusegraph.com/sdk/abusegraph.min.js"></script>
<script>
  const sdk = AbuseGraph.init({ publicKey: "pk_test_…" });

  async function collectForAuth(email, event = "signup") {
    const collected = await sdk.collect();
    const evaluated = await sdk.evaluate(collected, { email });
    return sdk.toCheckBody(collected, {
      email,
      event,
      verdictToken: evaluated.verdictToken?.token,
    });
  }
</script>

POST the returned JSON to your own endpoint. Never treat the browser result as the authoritative decision or put an sk_ key in browser code.

Better Auth hooks

Use before hooks to gate account and session creation. The browser payload may contain risk signals, so place trusted identity and event fields after it. This example returns false on a block, which aborts the pending write:

// auth.ts
databaseHooks: {
  user: {
    create: {
      before: async (user, ctx) => {
        const browserFields = ctx.body?.abusegraph ?? {}
        const result = await scoreAuth({
          ...browserFields,
          email: user.email,
          userId: user.id,
          event: "signup",
        })
        if (result.verdict === "block") {
          return false
        }
      },
    },
  },
  session: {
    create: {
      before: async (session, ctx) => {
        const result = await scoreAuth({
          email: ctx.user.email,
          userId: ctx.user.id,
          event: "login",
        })
        if (result.verdict === "block") {
          return false
        }
      },
    },
  },
}

Lifecycle events

  • login — session.create.before hook with event: "login" (the after hook is for monitoring only).
  • password_reset — call scoreAuth in your reset route with event: "password_reset".
  • email_change — call before applying the email update with event: "email_change".

Platform

Lists, rules, and linked accounts apply on every check — configure in the console. Optional session: POST /api/v1/session. Native apps: iOS, Android, React Native.

Verify

Sign up once with sk_test_…, then open Activity in the console. Blocked signups should not retain a session.