What to send for each event type
The more you send, the more signals fire — but only event_type and (for most
events) ip are hard requirements. Canonical payloads:
Field notes
device_token on card events
Not “recommended” in the ordinary sense: it is the only field whose absence removes enforcement rather than a signal — the two card rules that can block on their own both read a device-keyed counter. See device fingerprinting for exactly what goes dark. Per-request it stays optional (a browser that blocks collection must still be able to sign up); it is your integration that is incomplete without it.
ip — optional only on trial_convert
Conversions are usually recorded in a payment-provider webhook handler, where there is no client request — and therefore no honest client IP — in scope. The other events happen inside a user-initiated request, so send the IP: IP-family and velocity signals depend on it. Never send your own server’s IP as a stand-in — omit rather than lie.
Where login screening goes
After the credential check, before you grant the session. The ordering is the point, not a nicety:
- Pre-auth is wrong: before the password or OAuth exchange succeeds, “who is
logging in” is an unverified claim — anyone can type anyone’s email. You
would be baselining impostors into real accounts’ histories, and handing
attackers both a verdict oracle and a lockout tool (deliberately tripping
blockagainst a victim’s email). - Post-session is too late: once the browser holds session cookies there is nothing left to enforce.
So: let your auth provider verify the credential, fire the login verdict
with the now-verified external_user_id, and only issue the session on
allow/review. On block, deny that session only — don’t ban the
account: an account-takeover block means the attacker is at the door, and a
ban locks out the real owner too. A challenge
recommended action
means step up (MFA, re-auth) instead of refusing.
For a non-degraded result, persist the issued session’s identifier and event
family under result.id. If a later review is denied, revoke that session only.
The webhook does not include a session identifier; external_user_id or email
alone cannot distinguish a suspicious login from the owner’s other sessions.
If your auth stack cannot revoke an individual session, establish an explicit
session enforcement strategy before enabling asynchronous login review.
email on login
Every login mechanism just proved control of an identifier — you always have
the email, so send it. It is a hard identity key: it stitches the login into
the account’s cluster even when the device is new and the IP has rotated,
which is exactly the situation login screening exists for. (Older guidance
marked it unnecessary on the assumption external_user_id was always
present; send both when you can.)
identity{} — an open key set
Any string → string map: phone, payer_wallet, github_handle, whatever
identifiers your product collects. Unrecognized keys are stored and used for
identity linking; you don’t need to ask us before sending a new one.
payment{} — pass through from Stripe
payment_method.card.fingerprint → card_fingerprint,
payment_method.card.funding → card_funding,
payment_method.card.country → card_country (feeds
velocity_card_countries; needs SDK ≥ 0.3.1).
amount is integer minor units; currency is a 3-letter code (any case; we
normalize).
Signals that go dark without a field
An absent field is “unknown”, and unknown never fires a rule — so a missing field looks exactly like clean traffic. The three that catch people out:
Strict requests, tolerant emails
- Unknown top-level keys are rejected with a 400
invalid_request— an integration typo fails loudly in test mode instead of silently dropping a signal. Anything free-form belongs incontext{}, which is stored and echoed back and never read by rules — but it is bounded: at most 32 keys, keys up to 64 chars, 8 KB serialized total. It is a note field, not a document store. - Email is barely validated at the API edge (length +
@). On purpose: the engine judges email quality and reports it in reason codes; the schema just transports what your user typed. Don’t pre-filter emails before sending them.
Next
Device fingerprinting — the browser snippet that keeps the device-keyed rules alive. Not optional if you take payments.