Skip to main content

Preverus Python

Python client for Preverus backend enforcement.

Website: https://preverus.com
Documentation: https://preverus.com/docs

Use this package with Django, Flask, FastAPI, or any Python backend that loads the hosted Preverus browser script on the frontend and needs trusted server-side fraud decisions.

Install

pip install preverus

Requires Python 3.9+.

Browser And Server Flow

Add the hosted script to your HTML template:

<script
  src="https://api.preverus.com/v1/preverus.js"
  data-preverus-key="pk_live_xxx"
  data-preverus-auto="true"
  data-preverus-track-forms="true"
></script>

<form method="POST" action="/register" data-preverus-action="signup">
  <input name="email" type="email">
  <button type="submit">Create account</button>
</form>

Before submit, the script attaches:

preverus_fingerprint
preverus_visitor_id
preverus_risk_session_token
preverus_browser_session_event_id

Your backend sends those values to Preverus with a private server key before approving sensitive actions.

Quick Start

import os
from preverus import Client

client = Client(os.environ["PREVERUS_SERVER_KEY"])

decision = client.decisions.evaluate(
    {
        "event_type": "signup",
        "user_id": "acct_42",
        "ip": request.remote_addr,
        "risk_session_token": request.form.get("preverus_risk_session_token"),
        "fingerprint": request.form.get("preverus_fingerprint"),
        "metadata": {
            "email": request.form.get("email"),
            "browser_session_event_id": request.form.get("preverus_browser_session_event_id"),
        },
    },
    visitor_id=request.form.get("preverus_visitor_id"),
    idempotency_key="signup:acct_42:request-id",
)

if decision.is_block():
    abort(403)

if decision.is_review():
    return redirect("/verify")

# Continue signup.

Prefer risk_session_token when available. It links the backend action to the browser session collected moments earlier.

Configuration

client = Client(
    os.environ["PREVERUS_SERVER_KEY"],
    endpoint="https://api.preverus.com",
    timeout=1.5,
    retries=2,
    retry_delay=0.15,
    max_retry_delay=1.0,
)

The client retries transient network failures and retryable statuses:

408, 409, 425, 429, 500, 502, 503, 504

It does not retry validation or authentication errors such as 400, 401, 403, or 422.

Use idempotency keys for retried POST requests.

Django Example

from django.http import HttpResponseForbidden
from django.shortcuts import redirect
from preverus import Client

client = Client(settings.PREVERUS_SERVER_KEY)

def register(request):
    decision = client.decisions.evaluate(
        {
            "event_type": "signup",
            "user_id": request.POST.get("user_id"),
            "ip": request.META.get("REMOTE_ADDR"),
            "risk_session_token": request.POST.get("preverus_risk_session_token"),
            "fingerprint": request.POST.get("preverus_fingerprint"),
            "metadata": {
                "email": request.POST.get("email"),
                "user_agent": request.META.get("HTTP_USER_AGENT"),
            },
        },
        visitor_id=request.POST.get("preverus_visitor_id"),
    )

    if decision.is_block():
        return HttpResponseForbidden()

    if decision.is_review():
        return redirect("verify")

    # Continue registration.

Flask Example

from flask import abort, redirect, request
from preverus import Client

client = Client(os.environ["PREVERUS_SERVER_KEY"])

@app.post("/withdraw")
def withdraw():
    decision = client.decisions.evaluate(
        {
            "event_type": "withdraw",
            "user_id": current_user.id,
            "ip": request.remote_addr,
            "risk_session_token": request.form.get("preverus_risk_session_token"),
            "metadata": {
                "payment_address": request.form.get("payment_address"),
            },
        },
        visitor_id=request.form.get("preverus_visitor_id"),
    )

    if decision.is_block():
        abort(403)

    if decision.is_review():
        return redirect("/withdraw/review")

Decisions

decision.recommended_action
decision.is_allow()
decision.is_review()
decision.is_block()
decision.to_dict()

Recommended handling:

allow  -> proceed
review -> step-up auth, hold, or manual review
block  -> deny or hard challenge

Events

Use events for non-blocking fraud telemetry:

event = client.events.create(
    {
        "event_type": "login",
        "user_id": "acct_42",
        "ip": "203.0.113.10",
        "fingerprint": "fp_hash",
        "metadata": {"email": "person@example.com"},
    },
    visitor_id="v_abc123",
)

Lookups

visitor = client.visitors.lookup(visitor_id="v_abc123")
visitor = client.visitors.lookup(fingerprint="fp_hash")

metadata = client.metadata.lookup("email", "person@example.com")
graph = client.metadata.graph("v_abc123")

Use lookups for investigation and context. Use decisions.evaluate() for final enforcement.

Webhook Verification

valid = client.webhooks.verify(
    raw_body=request.get_data(),
    timestamp=request.headers.get("X-Fraud-Webhook-Timestamp", ""),
    signature_header=request.headers.get("X-Fraud-Webhook-Signature", ""),
    secret=os.environ["PREVERUS_WEBHOOK_SECRET"],
)

if not valid:
    abort(400)

Webhook delivery is at-least-once. Dedupe by X-Fraud-Webhook-Id or payload id.

You can also verify, parse, and dispatch by event type:

event = client.webhooks.construct_event(
    raw_body=request.get_data(),
    headers=dict(request.headers),
    secret=os.environ["PREVERUS_WEBHOOK_SECRET"],
)

if already_processed(event.id):
    return "", 204

client.webhooks.dispatch(
    event,
    {
        "decision.high_risk": lambda event: open_case(event.payload),
        "*": lambda event: log_webhook(event.type),
    },
)

The client verifies and parses the event, but your app should store processed event IDs in your database or cache.

Failure Handling

The Python core client raises exceptions for API and network failures:

from preverus import ApiError, NetworkError

try:
    decision = client.decisions.evaluate({...})
except ApiError as error:
    print(error.status_code, error.error_code)
except NetworkError:
    # Apply your app's fail-open, fail-review, or fail-closed policy.

For high-risk flows like withdrawals and payouts, a common policy is fail-review. For signup/login/checkout, many businesses prefer fail-open so the site keeps working during transient network failures.

Production Checklist

  • Keep PREVERUS_SERVER_KEY private.
  • Use a browser key only in templates/frontend code.
  • Prefer risk_session_token when present.
  • Include visitor_id as X-Visitor-ID through the client argument.
  • Send your real customer account ID as user_id.
  • Include IP and metadata such as email, phone, username, and payment address.
  • Use idempotency keys for retried POST requests.
  • Treat review as step-up/manual review, not as automatic allow.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

preverus-0.1.1.tar.gz (10.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

preverus-0.1.1-py3-none-any.whl (8.6 kB view details)

Uploaded Python 3

File details

Details for the file preverus-0.1.1.tar.gz.

File metadata

  • Download URL: preverus-0.1.1.tar.gz
  • Upload date:
  • Size: 10.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.2

File hashes

Hashes for preverus-0.1.1.tar.gz
Algorithm Hash digest
SHA256 41110486dab21cecbc500b12def72bbb3f4f9f8ec201fbaacd07454e946670b1
MD5 e81e8135cc346cfdda6bf25c354db519
BLAKE2b-256 281370058c74126f62ff73506ab7b86f31f502a7d07e03d80ee9749e4b9e756d

See more details on using hashes here.

File details

Details for the file preverus-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: preverus-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 8.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.2

File hashes

Hashes for preverus-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0fd0984961a4e2833cab4e0665505211efa6a938ed08e4916b7cb22066b99710
MD5 bf8f92baf28b8f8e237ff0e4a9870103
BLAKE2b-256 fe3e0b74296495a307f73c9edb2acaf27d5244f3e62189e8a0325a7d79d0a0e2

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.2

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page