@ruptjs/api
v3.3.0
Published
Server-side SDK for Rupt — know which users and agents you can trust.
Readme
@ruptjs/api
The server-side SDK for Rupt. Use it from your backend to fetch evaluations, look up users and their devices, suspend users, manage lists, and read challenge details. It signs requests with your project secret, so keep it on the server.
Built for Node and any other runtime with global fetch. If you want the browser-side SDK that fingerprints users and runs the evaluate flow, install @ruptjs/javascript instead.
Install
yarn add @ruptjs/api
# or
npm install @ruptjs/apiQuick start
import { RuptAPI } from "@ruptjs/api";
const rupt = new RuptAPI(process.env.RUPT_SECRET);
const evaluation = await rupt.getEvaluation(evaluationId);
if (evaluation.verdict === "deny") {
// block the action
}Your project secret lives in the Rupt dashboard. The browser-safe @ruptjs/javascript SDK is the right counterpart on your frontend.
Configuration
const rupt = new RuptAPI(process.env.RUPT_SECRET, {
domain: "api.rupt.dev", // default; HTTPS is added automatically
timeout: 10_000, // ms before a request times out (default 10s)
retries: 3, // retry attempts on network errors and 5xx (default 3)
maxRetryDelay: 5_000, // cap on the exponential backoff between retries
});domain accepts a bare host (api.rupt.dev) and prepends https://. If you pass a value that already starts with http:// or https://, it's used as-is. That's how you point at a local API:
new RuptAPI(secret, { domain: "http://localhost:8007" });Retries kick in for network errors and 5xx responses. 4xx errors and timeouts do not retry; you get the typed error back so your code can decide what to do.
Methods
Get an evaluation
const evaluation = await rupt.getEvaluation(evaluationId);
// {
// id, action, verdict, reasons, checks, risks,
// policy, challenge, fingerprint, user,
// geolocation, metadata, ...
// }The full server-side payload, including verdict, risk breakdown, policy that matched, and the user the evaluation is bound to. This data is not exposed to the browser SDK.
Each entry in risks carries id (the risk itself), definition (the risk definition it was raised against), name, severity, score, and indicators. Key off definition when you branch on a risk type: name is a display label and can be renamed in the dashboard.
Get a challenge
const challenge = await rupt.getChallenge(challengeId);
// { id, status, delivery_status, challenge_mode, user, createdAt, ... }Returns the customer-safe projection that the Rupt-hosted challenge page reads. The API strips server-only fields like api_key and code before responding.
List a user's devices
const devices = await rupt.getUserDevices({ user: "user_123" });
// [{ id, user, status, info, metadata, attached_at, ... }, ...]Update a user
await rupt.updateUser({
user: "user_123",
email: "[email protected]",
phone: "+15555550100",
metadata: { plan: "pro" },
groups: [{ id: "workspace_42", name: "Acme" }],
});Only the fields you pass are written. Anything you leave out is untouched.
Suspend or unsuspend a user
const user = await rupt.suspendUser({ user: "user_123" });
// { rupt_id, id, email, phone, suspended: true }
await rupt.unsuspendUser({ user: "user_123" });Suspending sets the flag the is_suspended policy condition reads, the same one the suspend verdict sets. Pair it with a policy that denies on that condition. Both methods throw a RuptApiError with status 404 when the user doesn't exist in your secret's environment.
Add to or remove from a list
await rupt.addToList({
list: "64f1c2e5a1b2c3d4e5f60718",
values: ["user_123", "user_456"],
comment: "Chargeback on order 4821",
ttl: 60 * 60 * 24 * 30,
});
// { added: 2, updated: 0, total: 14 }
await rupt.removeFromList({ list: "64f1c2e5a1b2c3d4e5f60718", values: "user_123" });
// { removed: 1, total: 13 }list is the list's ID from its URL in the dashboard. values takes one string or up to 500. Values must match the kind of list: user IDs for a user list, IPs for an IP list, and so on. ttl is in seconds and falls back to the list's default expiry.
Errors
Every method throws on non-2xx responses, network failures, and timeouts. Error subclasses let you branch without parsing message strings.
import {
RuptApiError,
RuptNetworkError,
RuptTimeoutError,
} from "@ruptjs/api";
try {
await rupt.getEvaluation(id);
} catch (err) {
if (err instanceof RuptApiError) {
// 4xx or 5xx response. err.status and err.body are populated.
} else if (err instanceof RuptNetworkError) {
// The request never reached the server. err.cause has the underlying error.
} else if (err instanceof RuptTimeoutError) {
// The timeout fired before we got a response.
} else {
throw err;
}
}Continuing after a timeout
When a request times out, the SDK aborts the wait but lets the underlying fetch keep running. If you want to wait for it anyway, await the continue promise on the timeout error:
try {
await rupt.updateUser({ user, metadata });
} catch (err) {
if (err instanceof RuptTimeoutError) {
// Give the request another shot before giving up.
const eventual = await err.continue;
}
}You can ignore continue and the dangling request will resolve or reject quietly. The SDK attaches a no-op handler so you won't see unhandled-rejection warnings.
Types
AddToListResult, Challenge, Device, Evaluation, EvaluationRisk, Group, RemoveFromListResult, RuptAPIOptions, and User are all exported. Bring them in directly if you want to type your wrappers around the client.
