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.

emailThe address the user typed.
ipThe client IP of the signup request (not your server's).
visitorThe attrelix_visitor value from the form.
user_agentThe signup request's User-Agent header.
external_idYour id for the account, when it already exists.
card_fingerprintA 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}.

401missing, unknown, revoked or wrong type of key (a pk_ key on /v1/check).
400the body is not JSON or has none of email, ip and visitor.
402the plan's monthly checks are used.
403the agent ran on an origin you did not allow.
429too 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.

CodePointsMeaning
invalid_email+50The address is not valid
disposable_email+50Disposable e-mail domain
no_mx+35The domain cannot receive mail
alias_of_existing+30Another spelling of a mailbox already seen
relay_email+10Forwarding relay address
plus_tag+5Plus tag or Gmail dots
tor_exit+35Tor exit node
datacenter_ip+25Datacenter or cloud address
vpn_ip+20VPN address
ip_signups_24h+103+ accounts from this IP in 24 h
ip_signups_24h_high+2510+ accounts from this IP in 24 h
subnet_burst+1510+ accounts from this subnet in 1 h
automation_webdriver+40WebDriver automation
headless_browser+40Headless browser
automation_cdp+10DevTools-protocol automation (Puppeteer, Playwright)
antidetect_browser+15Anti-detect browser (several disguises at once)
headless_viewport+15Window without browser chrome or tabs
webgl_os_mismatch+25Graphics card belongs to another operating system
memory_cpu_mismatch+10Core count and memory disagree
no_human_interaction+30Submitted instantly with no typing, pointer or touch
instant_signup+25Filled and submitted within a second of the page loading
os_mismatch+20Operating system contradicts itself
browser_mismatch+20Browser contradicts its user agent
fonts_mismatch+15Fonts do not match the claimed system
canvas_noise+15Randomized canvas fingerprint
software_renderer+15Software graphics (virtual machine or headless)
zero_window+15Window with no size
permissions_mismatch+15Permission states contradict each other
no_plugins+10Desktop Chrome without plugins
no_languages+10No browser languages
utc_timezone+5Clock set to UTC
token_missing0–100No visitor token (agent not loaded or blocked)
token_invalid+30Forged or foreign visitor token
token_replayed+35Visitor token already used
token_expired+10Expired visitor token
token_ip_mismatch+8Token collected on another network
token_ua_mismatch+15Token collected by another browser
linked_accounts+15Linked to other accounts
linked_accounts_3+30Linked to 3+ accounts
linked_accounts_10+45Linked to 10+ accounts
shared_card+30Card used by another account
device_burst+155+ 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.