A debounced phone-check hook and a risk badge for React, talking to your own backend.
React hook and components for Numra.
This package never holds an API key and never talks to Numra. It calls
your own backend — the one @getnumra/express (or @getnumra/fastify,
@getnumra/next, @getnumra/nuxt, numra/laravel) mounts for you.
Numra reads a shared fraud ledger. A key in a JavaScript bundle is readable by
anyone who opens dev tools, so there is no publishable key and no way to make
this package talk to the API directly. A test in this repo fails the build if
an apiKey or api.numra.ma ever appears in the source.
npm install @getnumra/react @getnumra/expressimport { useNumraCheck, RiskBadge } from '@getnumra/react';
function OrderRow({ phone }) {
const { data, isLoading, error } = useNumraCheck(phone);
if (error) return <span>Could not check this number</span>;
return <RiskBadge check={data} loading={isLoading} showScore />;
}Server side, once:
app.use('/api/numra', numraRouter({ apiKey, authorize: (req) => Boolean(req.session?.user) }));Debounced at 400ms and aborts superseded requests. Without that, typing a phone number fires a lookup per keystroke — each one billable, and the answer that lands last is often the answer to a prefix of what was typed.
useNumraCheck(phone, {
endpoint: '/api/numra', // where you mounted the router
enabled: true,
debounceMs: 400,
});Returns { status, data, error, isLoading, refetch }.
riskScore alone cannot tell a checked-and-clean customer from a complete
stranger — both come back low. On a cash-on-delivery store most buyers are
new, so this matters:
if (!data.isRated) {
// No history. Not a clean bill of health — just no evidence.
}<RiskBadge> already does this: an unrated number renders as "No history",
not "Low risk". A blacklisted number renders as "Blacklisted" even when its
band computed to MEDIUM — otherwise your page and the Numra control panel
disagree about the same number.
If you build your own badge, use the same logic:
import { riskStateFor, RISK_STATES } from '@getnumra/react';
const state = RISK_STATES[riskStateFor(data)];RiskBadge uses inline styles so it works with no stylesheet in any setup.
Every colour pair clears 4.5:1 contrast, enforced by a test in
@getnumra/browser — that is where
the colours are defined, so that is where the check belongs. Pass style to
override the container.
Every release is tagged and written up on the Releases page. The same history in one file is in CHANGELOG.md.
Bug reports and patches are welcome. CONTRIBUTING.md covers running the tests, the regression test a change is expected to bring with it, and which repository a given fix actually belongs in.
Vulnerabilities go privately to the address in SECURITY.md. Do not open a public issue for a security problem — a public report is a working exploit for every merchant running the released version until a fix ships.
Twelve packages, one contract. The server side holds the API key; the browser side calls the endpoint the server side mounts.
Server:
| Package | Repository |
|---|---|
@getnumra/core |
numra-js-core |
@getnumra/express |
numra-express |
@getnumra/fastify |
numra-fastify |
@getnumra/next |
numra-next |
@getnumra/nuxt |
numra-nuxt |
numra/numra-php |
numra-php |
numra/laravel |
numra-laravel |
Browser:
| Package | Repository |
|---|---|
@getnumra/browser |
numra-browser |
@getnumra/react |
numra-react — this repo |
@getnumra/vue |
numra-vue |
@getnumra/svelte |
numra-svelte |
@getnumra/angular |
numra-angular |
Documentation for all of them is at numra.ma/docs.
MIT