Docs
Add the agent to your signup page, call one endpoint from your server, act on the decision.
Quickstart
1. Add the agent to your signup page
It collects device signals and puts a short-lived visitor token in a hidden field named attrelix_visitor in every form on the page. It also exposes window.attrelix.token() for single-page apps.
<script src="https://attrelix.com/v1.js?v=0bbe74b52d" data-key="pk_live_…" async></script> // single-page apps: wait for the token before you submit const visitor = await window.attrelix.token();
2. Check the signup from your server
Send the e-mail, the client IP and the visitor token with your secret key. Optional: external_id (your user id), user_agent and card_fingerprint (for example Stripe's card.fingerprint).
curl https://attrelix.com/v1/check \ -H "Authorization: Bearer $ATTRELIX_SECRET" \ -H "Content-Type: application/json" \ -d '{"email":"jdoe+7@gmail.com","ip":"203.0.113.9","visitor":"vt1.…"}'
# Python r = requests.post("https://attrelix.com/v1/check", timeout=3, headers={"Authorization": f"Bearer {SECRET}"}, json={"email": email, "ip": ip, "visitor": form["attrelix_visitor"]}) decision = r.json().get("decision", "allow") if r.ok else "allow"
3. Act on the decision
allow: grant the free credits. review: ask for something that costs a farm money — a card, a phone number — or grant fewer credits. block: refuse the account or the credits. If the call fails, allow: never lock out real users because of us.
Keys and tokens
- Publishable key (pk_live_…): public by design. It sits in your page and can only collect signals, from the origins you allow in the dashboard.
- Secret key (sk_live_…): server only. It runs checks and reads results. We store only its hash: copy it when you create it.
- Visitor token: signed by us, valid for 15 minutes and for a single check, bound to your project. A token used twice, or from another network than the signup, raises the score.
POST /v1/check
Request body (JSON). Send at least one of email, ip or visitor.
| The address the user typed. | |
| ip | The client IP of the signup request (not your server's). |
| visitor | The attrelix_visitor value from the form. |
| user_agent | The signup request's User-Agent header. |
| external_id | Your id for the account, when it already exists. |
| card_fingerprint | A card fingerprint, when a card is added at signup. |
Response
decision is allow, review or block. score is 0–100, the sum of the reasons' points, capped. Every reason has a stable code you can log or show.
{
"id": "chk_3fJ8kQ2mN7pX1aZb",
"decision": "block",
"score": 100,
"reasons": [
{ "code": "linked_accounts_10", "points": 45, "detail": "14" },
{ "code": "alias_of_existing", "points": 30, "detail": "3 variants of jdoe@gmail.com" },
{ "code": "datacenter_ip", "points": 25, "detail": "aws" }
],
"email": { "normalized": "jdoe@gmail.com", "disposable": false, "relay": false, "mx": "yes", … },
"network": { "ip": "3.88.0.1", "type": "datacenter", "provider": "aws", "signups_24h": 2 },
"device": { "id": "dev_9c1e…", "token": "valid", "browser": "Firefox 144", "flags": ["canvas_noise"] },
"linked": { "accounts": 14, "by_device": 12, "by_card": 0, "cluster": "cl_7q2f…" },
"latency_ms": 38
}
Errors
Errors are JSON: {"error": code, "message": text}.
| 401 | missing, unknown, revoked or wrong type of key (a pk_ key on /v1/check). |
| 400 | the body is not JSON or has none of email, ip and visitor. |
| 402 | the plan's monthly checks are used. |
| 403 | the agent ran on an origin you did not allow. |
| 429 | too many requests from one address. |
Scoring
Scores add up and are capped at 100. Defaults: review from 50, block from 80 — both adjustable. A disposable inbox alone goes to review; no single device or network signal does.
| Code | Points | Meaning |
|---|---|---|
| invalid_email | +50 | The address is not valid |
| disposable_email | +50 | Disposable e-mail domain |
| no_mx | +35 | The domain cannot receive mail |
| alias_of_existing | +30 | Another spelling of a mailbox already seen |
| relay_email | +10 | Forwarding relay address |
| plus_tag | +5 | Plus tag or Gmail dots |
| tor_exit | +35 | Tor exit node |
| datacenter_ip | +25 | Datacenter or cloud address |
| vpn_ip | +20 | VPN address |
| ip_signups_24h | +10 | 3+ accounts from this IP in 24 h |
| ip_signups_24h_high | +25 | 10+ accounts from this IP in 24 h |
| subnet_burst | +15 | 10+ accounts from this subnet in 1 h |
| automation_webdriver | +40 | WebDriver automation |
| headless_browser | +40 | Headless browser |
| automation_cdp | +10 | DevTools-protocol automation (Puppeteer, Playwright) |
| antidetect_browser | +15 | Anti-detect browser (several disguises at once) |
| headless_viewport | +15 | Window without browser chrome or tabs |
| webgl_os_mismatch | +25 | Graphics card belongs to another operating system |
| memory_cpu_mismatch | +10 | Core count and memory disagree |
| no_human_interaction | +30 | Submitted instantly with no typing, pointer or touch |
| instant_signup | +25 | Filled and submitted within a second of the page loading |
| os_mismatch | +20 | Operating system contradicts itself |
| browser_mismatch | +20 | Browser contradicts its user agent |
| fonts_mismatch | +15 | Fonts do not match the claimed system |
| canvas_noise | +15 | Randomized canvas fingerprint |
| software_renderer | +15 | Software graphics (virtual machine or headless) |
| zero_window | +15 | Window with no size |
| permissions_mismatch | +15 | Permission states contradict each other |
| no_plugins | +10 | Desktop Chrome without plugins |
| no_languages | +10 | No browser languages |
| utc_timezone | +5 | Clock set to UTC |
| token_missing | 0–100 | No visitor token (agent not loaded or blocked) |
| token_invalid | +30 | Forged or foreign visitor token |
| token_replayed | +35 | Visitor token already used |
| token_expired | +10 | Expired visitor token |
| token_ip_mismatch | +8 | Token collected on another network |
| token_ua_mismatch | +15 | Token collected by another browser |
| linked_accounts | +15 | Linked to other accounts |
| linked_accounts_3 | +30 | Linked to 3+ accounts |
| linked_accounts_10 | +45 | Linked to 10+ accounts |
| shared_card | +30 | Card used by another account |
| device_burst | +15 | 5+ checks from this device in 1 h |
Data sources
Disposable domains: the disposable-email-domains project (CC0) and disposable/disposable-email-domains (MIT), refreshed daily. Datacenter ranges: the published lists of AWS, Google Cloud, Oracle Cloud, DigitalOcean and Linode. Tor: the Tor Project's exit list, refreshed hourly.
Right now: 98,644 disposable domains and 21,665 ranges.