Keys and your first verdict
Keys
Keys live in Settings → API keys in the dashboard. Your account has live and test keys, and they come in two kinds:
Server — secret keys
Used for the Verdict, feedback, and event APIs. These stay on your server (secrets manager); never expose them in client-side code.
Browser — publishable keys
Used only to mint device tokens. Safe to ship to the browser. See device fingerprinting.
Test mode is fully isolated. Test events, velocity counters, and clusters never affect live data — same engine, separate world.
Install
Node 18.17+. The SDK has zero runtime dependencies; types come from the published API contract. Not on Node? Skip to REST — the API is four endpoints.
First verdict
The same screening, in the two most common shapes:
Next.js route handler
Express middleware
The three decisions both shapes encode:
- Call before you create the account, with the id you are about to use as
external_user_id. That id is how Portreeve links events to accounts, and how a laterreview.resolvedwebhook tells you which user to revoke. - On
block, return a generic error. Don’t tell abusers what tripped them. - On
review(andallow), proceed — and persistresult.idwith the user, guarding onresult.degradedfirst: a degraded verdict has no server event, so itsidisnull(see failure behavior).
Client options and failure behavior
new Portreeve(secretKey, options?):
Fail-open by default
With failMode: "allow" (or "block"), .verdict() never throws — an
outage on our side degrades to your chosen verdict instead of breaking your
signup or checkout. This is deliberate: your revenue path matters more than any
single verdict. A synthesized result is marked:
degraded: true;idandpolicy_versionarenull— there is no server event behind them, so guard onresult.degradedbefore persistingresult.id(both quickstarts above show the pattern);- the reasons carry a
*_failopencode (timeout_failopen,network_failopen, … — see SDK-synthesized codes), so your logs can tell a real verdict from a fail-open one.
When (and whether) to switch checkout to fail-closed: Handling verdicts.
Safe retries
Retrying a verdict call (or replaying a job): pass
{ idempotencyKey: "signup-usr_9f2c" } — retries with the same key return the
original verdict and don’t double-count velocity signals. Details under
Idempotency.
Next
What to send for each event type — the canonical payload per event, and the fields whose absence silently switches signals off.