Preverus Ruby
Ruby client for Preverus backend enforcement.
Use this gem with Rails, Rack, Sinatra, Hanami, or any Ruby backend that loads the hosted Preverus browser script and needs trusted server-side fraud decisions.
Install
Add to your Gemfile:
gem "preverus"
Then run:
bundle install
Requires Ruby 3.0+.
Browser And Server Flow
Add the hosted script to your layout:
<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
require "preverus"
client = Preverus::Client.new(server_key: ENV.fetch("PREVERUS_SERVER_KEY"))
decision = client.decisions.evaluate(
{
event_type: "signup",
user_id: "acct_42",
ip: request.remote_ip,
risk_session_token: params[:preverus_risk_session_token],
fingerprint: params[:preverus_fingerprint],
metadata: {
email: params[:email],
browser_session_event_id: params[:preverus_browser_session_event_id]
}
},
visitor_id: params[:preverus_visitor_id],
idempotency_key: "signup:acct_42:request-id"
)
if decision.block?
head :forbidden
return
end
if decision.review?
redirect_to verify_path
return
end
# Continue signup.
Prefer risk_session_token when available. It links the backend action to the browser session collected moments earlier.
Configuration
client = Preverus::Client.new(
server_key: ENV.fetch("PREVERUS_SERVER_KEY"),
endpoint: "https://api.preverus.com",
timeout: 1.5,
retries: 2,
retry_delay: 0.15,
max_retry_delay: 1.0
)
The gem 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.
Rails Setup
Create an initializer:
# config/initializers/preverus.rb
PREVERUS = Preverus::Client.new(
server_key: ENV.fetch("PREVERUS_SERVER_KEY"),
timeout: 1.5,
retries: 2
)
Use it in a controller:
class RegistrationsController < ApplicationController
def create
decision = PREVERUS.decisions.evaluate(
{
event_type: "signup",
user_id: params[:user_id],
ip: request.remote_ip,
risk_session_token: params[:preverus_risk_session_token],
fingerprint: params[:preverus_fingerprint],
metadata: {
email: params[:email],
user_agent: request.user_agent,
browser_session_event_id: params[:preverus_browser_session_event_id]
}
},
visitor_id: params[:preverus_visitor_id],
idempotency_key: request.request_id
)
return head :forbidden if decision.block?
return redirect_to verify_path if decision.review?
# Continue registration.
end
end
Sensitive Actions
Use decisions synchronously when your app needs an immediate allow/review/block answer:
decision = PREVERUS.decisions.evaluate(
{
event_type: "withdraw",
user_id: current_user.id,
ip: request.remote_ip,
risk_session_token: params[:preverus_risk_session_token],
metadata: {
payment_address: params[:payment_address]
}
},
visitor_id: params[:preverus_visitor_id],
idempotency_key: request.request_id
)
return head :forbidden if decision.block?
return redirect_to withdraw_review_path if decision.review?
Good event names include:
signup
login
checkout
password_reset
payout
withdraw
payment_change
account_update
Decisions
decision.recommended_action
decision.allow?
decision.review?
decision.block?
decision.to_h
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 = PREVERUS.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"
)
In Rails, send non-blocking events from Active Job or a background worker so customer requests do not wait for telemetry.
Lookups
visitor = PREVERUS.visitors.lookup(visitor_id: "v_abc123")
visitor = PREVERUS.visitors.lookup(fingerprint: "fp_hash")
= PREVERUS..lookup("email", "person@example.com")
graph = PREVERUS..graph("v_abc123")
Use lookups for investigation and context. Use decisions.evaluate for final enforcement.
Webhook Verification
class PreverusWebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def create
raw_body = request.raw_post
valid = PREVERUS.webhooks.verify(
raw_body: raw_body,
timestamp: request.headers["X-Fraud-Webhook-Timestamp"].to_s,
signature_header: request.headers["X-Fraud-Webhook-Signature"].to_s,
secret: ENV.fetch("PREVERUS_WEBHOOK_SECRET")
)
return head :bad_request unless valid
payload = JSON.parse(raw_body)
# Store payload["id"] or X-Fraud-Webhook-Id for idempotency.
head :no_content
end
end
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 = PREVERUS.webhooks.construct_event(
raw_body: request.raw_post,
headers: request.headers,
secret: ENV.fetch("PREVERUS_WEBHOOK_SECRET")
)
return head :no_content if already_processed?(event.id)
PREVERUS.webhooks.dispatch(event, {
"decision.high_risk" => ->(event) { open_case(event.to_h) },
"*" => ->(event) { Rails.logger.info("Preverus webhook #{event.type}") }
})
The gem verifies and parses the event, but your app should store processed event IDs in your database or cache.
Failure Handling
The Ruby core gem raises exceptions for API and network failures:
begin
decision = PREVERUS.decisions.evaluate(...)
rescue Preverus::ApiError => error
Rails.logger.warn("Preverus API error: #{error.status_code} #{error.error_code}")
rescue Preverus::NetworkError
# Apply your app's fail-open, fail-review, or fail-closed policy.
end
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.
Example fail-review wrapper:
def evaluate_or_review(input, **)
PREVERUS.decisions.evaluate(input, **)
rescue Preverus::Error => error
Rails.logger.warn("Preverus fallback review: #{error.class}")
Preverus::DecisionResult.fallback("review", error.class.name)
end
Production Checklist
- Keep
PREVERUS_SERVER_KEYprivate. - Use a browser key only in templates/frontend code.
- Prefer
risk_session_tokenwhen present. - Include
visitor_idthrough 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
reviewas step-up/manual review, not as automatic allow.