npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cashoutguard/node

v1.0.0

Published

Node.js client for the CashoutGuard fraud-prevention API (rewards, GPT and offerwall sites).

Readme

@cashoutguard/node

Node.js client for CashoutGuard, the fraud-prevention API for rewards, GPT and offerwall sites.

  • Node 18 or newer. No runtime dependencies: it uses the global fetch and crypto. TypeScript types are included.
  • Fail-open by default. A timeout, network error, non-2xx answer, invalid JSON or invalid argument resolves to an allow result with error set. It never throws.
  • 2 second total timeout by default.

Install

npm install @cashoutguard/node

Quickstart

1. Browser: on every page

<script src="https://cashoutguard.com/v1/agent.js" data-key="pk_live_YOUR_PUBLIC_KEY" async></script>

Collect a request_id on the key moment and send it with your form:

CashoutGuard.ready(async () => {
  const { request_id } = await CashoutGuard.collect({ event: 'cashout' });
  document.querySelector('#cashout-form [name=cg_request_id]').value = request_id ?? '';
});

2. Server: ask for a decision

import { CashoutGuard } from '@cashoutguard/node';      // or: const { CashoutGuard } = require('@cashoutguard/node');

const cg = new CashoutGuard(process.env.CASHOUTGUARD_SECRET);

app.post('/cashout', async (req, res) => {
  const risk = await cg.evaluate({
    event: 'cashout',                  // signup | login | offer_click | conversion | cashout | custom
    account_id: req.user.id,           // your own user id
    request_id: req.body.cg_request_id,
    ip: req.ip,
    email: req.user.email,
    payout_address: req.body.paypal_email,
    payout_method: 'paypal',
    amount: Number(req.body.amount),
    currency: 'USD',
  });

  if (risk.failed) console.warn('CashoutGuard unavailable, failing open:', risk.error);

  if (risk.isBlocked()) return res.status(403).json({ error: 'on_hold' });
  if (risk.needsReview()) await queueForReview(req.user, risk.raw);
  else await payNow(req.user, req.body.amount);
  res.sendStatus(204);
});

Offerwall conversions (postbacks)

Score the conversion from your postback handler. Send the same click_id you used on offer_click; no request_id is needed because the conversion inherits the device of its click.

const risk = await cg.evaluate({
  event: 'conversion',
  account_id: userId,          // networks: "publisherId:userId"
  offer_id: offerId,
  offer_name: offerTitle,      // shown in the dashboard
  click_id: clickId,
  transaction_id: txId,
  amount: payout,
  ip: userIp,                  // if the offerwall sends it
  source: 'torox',             // offerwall, network or publisher
  meta: { sub1 },              // up to 20 extra strings
});

Result

| Member | Meaning | |---|---| | isBlocked() / needsReview() / isAllowed() | Based on action. Use these. | | action | allow, review or block: what to do. In monitor mode it is always allow. | | decision | allow, review or block: what the rules concluded, even in monitor mode. | | score | 0–100, or null when the call failed. | | reasons / reasonCodes() / hasReason(code) | [{ code, weight, detail? }], highest weight first. | | raw | Full JSON response: account, device, network, linked_accounts, cashout, and so on. | | eventId, mode, account, accountStatus() | Convenience accessors. | | ok / failed, error, errorMessage, status | error is null on success. Otherwise it is a short code such as timeout, network_error, http_500, temporarily_unavailable, missing_or_invalid_secret_key, validation_failed, not_found, invalid_argument, invalid_response or missing_secret_key. |

evaluate() sends only the documented fields. It drops optional values that the API would reject, such as a malformed IP or request_id, or a negative amount, and it truncates over-long strings.

Accounts

const acc = await cg.account('user_48213');              // GET /v1/accounts/{id}
if (acc.error === 'not_found') { /* never evaluated */ }

await cg.setAccountStatus('user_48213', 'blocked');      // active | allowlisted | blocked

Webhooks

Each webhook is signed with X-CashoutGuard-Signature = hex HMAC-SHA256 of the raw body, keyed with your webhook secret. The check uses crypto.timingSafeEqual.

import express from 'express';
import { verifyWebhook } from '@cashoutguard/node';

app.post('/webhooks/cashoutguard', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifyWebhook(req.body, req.get('X-CashoutGuard-Signature'), process.env.CASHOUTGUARD_WEBHOOK_SECRET)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body.toString('utf8'));   // { type: 'account.blocked', created_at, data: { account_id, score, event_id } }
  res.sendStatus(204);
});

Options

new CashoutGuard('sk_live_...', {
  baseUrl: 'https://cashoutguard.com', // default
  timeoutMs: 2000,                     // whole request
  failOpen: true,                      // false = reject with CashoutGuardError { code, status, body }
  fetch: customFetch,                  // default: global fetch
});

Development

npm install
npm test        # tsc -> dist/, then node --test

License

MIT