Skip to main content
Signup is the highest-leverage place to run Foil. Every downstream abuse pattern - spam, scraping, ratings manipulation, trial farming - starts with a bot-created account. A hard block here is cheaper than cleaning up later.

The threat

Automated signup is the most common and most costly bot surface. Attackers create accounts in bulk to seed spam networks, farm free-trial credits, inflate referral payouts, reserve usernames, drain rate limits, or build a corpus of “aged” accounts for later sale. Because account creation is only occasional for legitimate users, you can afford a stricter policy here than on, say, a login page that a human might hit dozens of times a week. Foil fits the shape of this problem well: you want a strong verdict before the row is written. The browser client streams observations the whole time a user is filling out the form, and getSession() flushes everything into a sealed handoff right at submit. Your backend verifies the token, checks the verdict, and decides whether to proceed - all before you touch the database. Two detection categories matter most on this surface:
  • automation - Playwright, Puppeteer, Selenium, Patchright, and related stealth tooling driving a headless browser.
  • ai-agent - LLM-powered agents that operate a browser to complete signup end to end.
Foil detects both. On a signup page, it is reasonable to hard-block either.

The flow

1

Start Foil on page load

The browser client begins collection immediately so a full fingerprint is available by the time the user submits.
2

Request a sealed handoff at submit

getSession() waits for fingerprinting to finish, flushes the latest observations, and returns { sessionId, sealedToken }.
3

Verify on the backend

Use safeVerifyFoilToken() with your secret key to verify the token locally, with no extra network call, and check that the token is recent.
4

Apply policy

Hard-block on bot, step up on inconclusive, proceed on human.

Client integration

Start Foil when the page loads, and call getSession() when the user submits the form. getSession() waits for fingerprinting to finish before it returns a handoff, so a user who submits quickly doesn’t need a disabled button or a call to waitForFingerprint(). The example disables the submit button while the request is in flight so that a double click doesn’t create two accounts, and it enables the button again when the request finishes.
If the script fails to load or getSession() fails, the page sends the form without a handoff, so a blocked script doesn’t leave the form unresponsive. The next section describes how the server treats a request without a valid token.

Server verification

Decisioning policy

Three policy choices worth making deliberately:
  • Fail closed when verification fails. Return an error when the token is missing or invalid. Login fails closed in the same way (see Login & credential stuffing). A real user can refresh and retry, and an attacker can’t bypass Foil by omitting the token or sending a malformed one.
  • Reject stale tokens. The server SDKs verify a token’s contents but don’t check its age. Reject a token whose decision.evaluated_at is more than a few minutes old, so that an attacker can’t replay a token captured from one session. See Verify the token on your server for the check in each language.
  • Do not leak detection to the client. Return the same message for bot and a generic server error. The browser client never receives verdicts directly - keeping them invisible at the response layer too means attackers get no signal to iterate against.
If you run the report-only mode from Going to production on signup, log decision.verdict and the actor label to see what you would block before you turn enforcement on. The actor label is the entry in attribution.bot.labels whose kind is actor, and its value is automation, ai-agent, crawler, or unknown. Some sessions have no actor label, so treat a missing label as unknown.
Node.js
The API abuse & scraping guide shows this lookup in each language.

What’s next

Server verification

Reference for the underlying verification primitive.

Going to production

Rollout plan - report-only, soft challenge, hard enforcement.

Login protection

Protect the login form against credential stuffing.

KYC fraud

Stack with your KYC vendor to catch identity farming.