Back to Blog

Add a trial and credit abuse check to your signup flow

Step-by-step guide to adding a free trial and signup credit abuse check to your signup flow with the AbuseZero Node.js SDK or REST API. One call returns allow or deny.

The best place for a free trial abuse check is the exact moment your application is about to grant the benefit. Not after the trial has started, and not in a nightly report. This guide shows where the check goes in your signup flow, how to add it with the AbuseZero Node.js SDK or REST API, and how to roll it out without affecting a single real user on day one.

You keep your signup flow. You add one server-side call. AbuseZero returns allow or deny.

Without a check at that moment, the first sign of abuse is usually the bill: a spend alert at 3 a.m., a queue backed up with accounts that will never pay, or a free tier that's suddenly slow for everyone.

From our early beta

A team whose cloud bill normally ran $200 to $300 a month woke up to a $12,000 bill. Automated registrations had triggered thousands of expensive serverless functions, database operations and background jobs overnight.

Evaluate before you grant

The principle is simple: evaluate the claim, then grant the benefit. A benefit can be a trial, signup credits, a free plan or a promotion. Whatever it is, the check belongs at the point where your server is about to hand it out.

[deny]alt[allow]opt[Benefit set to report outcomes]Starts a trialCollects a device tokenClaim with device tokenEvaluate the claimallow or deny, risk score and reasonGrant the trialTrial startedTrial not availableReport the outcomeUserYour pageYour serverAbuseZeroUserYour pageYour serverAbuseZero
[deny]alt[allow]opt[Benefitset toreportoutcomes]Startsa trialCollectsadevicetokenClaim withdevice tokenEvaluatethe claimallow ordeny, riskscore andreasonGrantthetrialTrial startedTrial not availableReport theoutcomeUserYourpageYourserverAbuseZeroUserYourpageYourserverAbuseZero

Checking at the claim, rather than only at account creation, protects each benefit on its own terms. A user might sign up today and claim credits next week, or claim a second benefit later. For why this matters to the economics, see the real cost of free trial abuse.

What you need

  • An AbuseZero workspace. Start with Builder and pay as you go.
  • A secret key from Developers → API keys, stored on your server as ABUSEZERO_SECRET_KEY.
  • Node.js 22 or later for the SDK examples. Any other language can call the AbuseZero REST API directly.

Add the check, step by step

Install the AbuseZero package

npm install abusezero

One package covers your server (abusezero/server) and your pages (abusezero/browser), with separate entry points so server code never loads in the browser.

Create the benefit in observe mode

A benefit is what you protect. Start it in observe mode: every claim is allowed while AbuseZero records what it would have decided.

scripts/create-benefit.ts
const benefit = await abusezero.createBenefit({
  name: "Pro trial",
  key: "pro_trial",
  policy_key: "balanced",
  protection_mode: "observe",
  config: {
    eligibility: { kind: "once" },
  },
}, "create-benefit-pro-trial");

You can also create benefits in the dashboard, or with abusezero benefits create in the AbuseZero CLI.

Collect a device token on your page

The AbuseZero browser SDK connects the claim to AbuseZero's device and automation intelligence. Use the same claim ID in the browser and on your server.

app/trial-form.ts
import { init, observe } from "abusezero/browser";

init({ key: "pk_…" }); // once, when your app starts

const { deviceToken } = await observe({ key: "pk_…", claimId: trialId });
// Send deviceToken to your server with the claim.

Evaluate the claim on your server

Call AbuseZero before you grant the benefit. The idempotency key makes the call safe to retry.

server/start-trial.ts
import { AbuseZero } from "abusezero/server";

const abusezero = new AbuseZero({
  key: process.env.ABUSEZERO_SECRET_KEY!,
  outage: "throw",
});

export async function startTrial(
  user: { id: string; email: string; emailVerified: boolean },
  trialId: string,
  ip: string,
  deviceToken: string,
) {
  const evaluation = await abusezero.evaluate(
    {
      benefit: "pro_trial",
      claim_id: trialId,
      subject: { external_id: user.id, email: user.email, email_verified: user.emailVerified },
      context: { ip },
      identifiers: { device_token: deviceToken },
    },
    `evaluate-${trialId}`,
  );

  if ("status" in evaluation) {
    // Skipped: no allow or deny was decided.
    return { started: false };
  }

  if (evaluation.result === "deny") {
    return { started: false };
  }

  // Grant the trial in your application here.
  return { started: true };
}

The response is one final result:

{
  "object": "evaluation",
  "id": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
  "result": "allow",
  "risk": { "score": 10, "band": "low" },
  "reason": { "code": "default_allow", "message": "No rule matched, so the policy's default allowed this claim." },
  "outcome": { "state": "assumed_granted", "due_at": null }
}

Act on the result

Branch on result and keep the reason for your team. For credits, a denial can lead to a paid starter pack, as the AI credits playbook shows. Show denied users a neutral message with a way to reach you, and never explain what was detected. We cover the wording in multi-accounting detection without blocking real users.

A skipped evaluation, for example when a workspace has no credits left, is its own branch: no allow or deny was decided. See skipped evaluations.

Switch to protect

When the observe-mode results look right, set the benefit's mode to protect. Your code doesn't change. Before you do, you can replay a policy change against your recent traffic. See test changes safely. The full rollout is in shadow mode for abuse rules.

Ship the check today

The integration is one package and one server call. Start with Builder and protect your first benefit, or see pricing for Pro and Scale.

Grant now, evaluate in the background

For a benefit you can take back in full, you can let the user continue at once and evaluate in a background job. On deny, you take the benefit back. See zero-wait free trials.

BenefitPattern
Trial days, plan access, a featureGrant now, evaluate in the background. Take it back on deny.
AI or API credits, payouts, referral cash, promotional codesEvaluate before you grant. These can be used up at once.

The job can act on the response itself, or on the evaluation.completed webhook event. See evaluate in the background. To pick the right pattern for each offer, see how to prevent free trial abuse. More in AbuseZero webhooks.

Decide what happens if AbuseZero can't be reached

Every evaluation returns one final result within the evaluation's time limit, and evidence that is slow or unavailable doesn't hold it up. For the rare case where your server can't reach AbuseZero at all, choose your behavior up front with the SDK's outage option:

throwallowdenyAbuseZero can't bereachedoutage optionYour code decidesUsers keep getting thebenefitNothing is granted untilAbuseZero answers
throwallowdenyAbuseZero can't bereachedoutage optionYour code decidesUsers keep getting thebenefitNothing is granted untilAbuseZero answers

Reuse the same idempotency key on every retry, so a retry never creates a second evaluation. See handle outages and retries. To choose per benefit, see fail open or fail closed?

Use the tool your team prefers

Every action is available through the AbuseZero REST API, the AbuseZero Node.js SDK, the AbuseZero CLI and the AbuseZero MCP server, with the same inputs and the same results. Connect the AbuseZero MCP server to your AI coding agent and it can create benefits, run previews and read evaluations for you while you build. See AbuseZero MCP server and CLI.

Before launch, run through the go-live checklist.

Frequently asked questions

Should I check for trial abuse at signup or when the trial starts?

Check when the benefit is granted. If your trial starts at signup, that's the same moment. If users can sign up first and claim later, evaluate at the claim, so each benefit is protected on its own terms.

Will an abuse check slow down my signup flow?

The evaluation is one server-side call that returns within its time limit, with everything AbuseZero gathered in that window. For benefits you can take back, you can also grant at once and evaluate in the background.

Which languages does AbuseZero support?

Any language that can make an HTTPS request can use the AbuseZero REST API. The AbuseZero Node.js SDK adds typed methods, retries and outage handling for JavaScript and TypeScript servers.

Do I need the browser SDK?

The browser SDK is optional, and it gives every evaluation more evidence to work with. AbuseZero works from your server alone, and adding browser protection on the pages where claims start strengthens every check.

Can I set up AbuseZero from an AI coding agent?

Yes. The AbuseZero MCP server works with standards-compliant MCP clients, so your agent can manage benefits and run previews through the same actions as the API.

Add your first abuse check

Keep reading