Skip to main content

Python client for Preverus fraud decisions, events, lookups, and webhooks.

Project description

Preverus Python

Python client for Preverus backend enforcement.

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://cdn.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.

Project details


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.0.tar.gz (10.2 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.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: preverus-0.1.0.tar.gz
  • Upload date:
  • Size: 10.2 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.0.tar.gz
Algorithm Hash digest
SHA256 c0ab2fa452352633a91bbfc400bc8f84b556aa64007b26d42f4fd143465b40ac
MD5 d5eda09448c0912b5a8903c6e4ffb60f
BLAKE2b-256 5064b576b64a79ce4dc78b4499b13e398fefe755a906d27807c796aeb4502e70

See more details on using hashes here.

File details

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

File metadata

  • Download URL: preverus-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.5 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 63e03f3f55b1004582fee1205084dea0a8d9c450bdd4161493a5be23b6cfc47a
MD5 47dc9ae483652d64190c21d8dd01429a
BLAKE2b-256 a6e03234df69dc01c57a2cd51768991439ff6345a442b0fb38ab5889785a5c9b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page