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

AbuseGraph + Cloudflare Workers

Browser SDK on Pages — Worker proxies toCheckBody to abusegraph.com with sk_ secrets.

Quick path

  1. Init the browser SDK with pk_test_… on your site.
  2. Store ABUSEGRAPH_SECRET_KEY and ABUSEGRAPH_SITE as Worker secrets.
  3. Worker receives toCheckBody from the client and POSTs to https://abusegraph.com/api/v1/check.

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

Worker secrets (set via your Workers secrets UI or CLI):

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

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

Server check

Workers use fetch — POST to https://abusegraph.com/api/v1/check. Events: signup | login | password_reset | email_change. Optional sessionId.

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.

Cloudflare Worker proxy

Route the signup and login actions separately so the Worker chooses the event from the route, not from untrusted browser JSON. Invoke this check in the server-side signup/login handler before creating the account or session; a browser-only call is not an enforcement boundary.

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 })
    }

    const path = new URL(request.url).pathname
    const event = path === "/api/signup-risk-check"
      ? "signup"
      : path === "/api/login-risk-check"
        ? "login"
        : null
    if (!event) return new Response("Not found", { status: 404 })

    const { email, ...browserFields } = await request.json()
    const res = await fetch(
      env.ABUSEGRAPH_CHECK_URL ?? "https://abusegraph.com/api/v1/check",
      {
        method: "POST",
        headers: {
          "content-type": "application/json",
          "x-api-key": env.ABUSEGRAPH_SECRET_KEY, // sk_test_…
          "x-abusegraph-site": env.ABUSEGRAPH_SITE,
        },
        body: JSON.stringify({
          ...browserFields,
          email,
          site: env.ABUSEGRAPH_SITE,
          event,
        }),
      },
    )

    if (res.status === 402) {
      return new Response("credits_exhausted", { status: 402 })
    }

    if (!res.ok) {
      return new Response("Risk check unavailable", { status: 502 })
    }

    const result = await res.json()
    if (result.verdict === "block") {
      return Response.json(result, { status: 403 })
    }
    if (result.verdict === "challenge") {
      return Response.json(result, { status: 428 })
    }

    return Response.json(result)
  },
}

Client:

const browserFields = await collectForAuth(email, "signup")
const response = await fetch("/api/signup-risk-check", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ ...browserFields, email }),
})
if (!response.ok) throw new Error("Signup requires review")
// Your server-side signup handler must also gate account/session creation.

Lifecycle events

  • login — POST with event: "login" before session create.
  • password_reset — proxy with event: "password_reset".
  • email_change — check 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

Deploy with sk_test_…, submit signup, confirm the Worker returns a verdict and Activity shows the check.