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

AbuseGraph + Clerk

Check Clerk signup and login webhooks with the HTTPS API and optional browser signals.

Quick path

  1. Add sk_test_…, pk_test_…, and ABUSEGRAPH_SITE to env.
  2. Collect browser signals and send toCheckBody to your server before starting signup/sign-in.
  3. Call your backend before Clerk signup/sign-in to enforce the result; use webhooks for audit notifications.

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.

Clerk webhooks

user.created (signup audit)

This webhook runs after user creation, so it cannot prevent signup. Run the same check through your app's backend before calling Clerk's signup flow when enforcement is required.


export async function POST(req: Request) {
  const evt = await req.json()
  if (evt.type !== "user.created") return new Response("ok")

  const email = evt.data.email_addresses?.[0]?.email_address
  const browser = evt.data.unsafe_metadata?.abusegraph ?? {}

  const result = await scoreAuth({
    ...browser,
    email,
    userId: evt.data.id,
    event: "signup",
    ip: req.headers.get("x-forwarded-for") ?? undefined,
  })

  if (result.verdict === "block") {
    // Notify your review workflow; this webhook is post-creation.
  }

  return Response.json(result)
}

session.created (login audit)

This webhook runs after a session is created. Check through your backend before completing sign-in when the verdict must gate access.

if (evt.type === "session.created") {
  const email = evt.data.user?.email_addresses?.[0]?.email_address
  const result = await scoreAuth({
    email,
    userId: evt.data.user_id,
    event: "login",
    ip: req.headers.get("x-forwarded-for") ?? undefined,
  })
  if (result.verdict === "block") {
    // Revoke session / ban user
  }
}

Lifecycle events

  • login — session.created webhook with event: "login".
  • password_reset — call check from your reset handler or a custom webhook with event: "password_reset".
  • email_change — check before applying the Clerk 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

Call your server gate before signup/sign-in with a test key; confirm allowed checks in Activity and ensure blocked requests never create an account/session. Webhook notifications are post-event audit only.