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

@painitehq/sdk

v1.1.4

Published

Decision API for AI abuse prevention.

Readme

@painitehq/sdk

AI abuse prevention infrastructure for AI SaaS companies. PainiteHQ sits between your application and your AI provider calls. Every credit grant, expensive AI action, and agent run passes through PainiteHQ before cost is incurred. It returns a structured decision — allow, deny, throttle, require verification, or cap usage — before the spend happens.

Works with any AI provider: Anthropic, OpenAI, Google Gemini, OpenRouter, Replicate, Fal, or custom endpoints.


Install

npm install @painitehq/sdk

Quickstart

With Anthropic

import Anthropic from "@anthropic-ai/sdk";
import { Painite } from "@painitehq/sdk";

const painite = new Painite({ apiKey: process.env.PAINITE_API_KEY! });
const anthropic = new Anthropic();

// Before the AI call — estimate cost from token counts
const decision = await painite.protect({
  accountId: user.id,
  workspaceId: user.workspaceId,
  action: "chat_completion",
  feature: "support_agent",
  provider: "anthropic",
  model: "claude-opus-4-8",
  estimatedInputTokens: 5000,
  estimatedOutputTokens: 2000,
  estimatedCostUsd: 0.075,
  prompt: userPrompt,
});

if (!decision.allowed) {
  return Response.json({ error: decision.recommendedAction }, { status: 403 });
}

// Proceed with your AI call
const result = await anthropic.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 8192,
  messages: [{ role: "user", content: userPrompt }],
});

// Report actual usage
await painite.recordUsage({
  decisionRequestId: decision.requestId,
  accountId: user.id,
  action: "chat_completion",
  feature: "support_agent",
  provider: "anthropic",
  model: "claude-opus-4-8",
  inputTokens: result.usage?.input_tokens,
  outputTokens: result.usage?.output_tokens,
  costUsd: (result.usage?.input_tokens ?? 0) * 3 / 1_000_000
         + (result.usage?.output_tokens ?? 0) * 15 / 1_000_000,
});

With OpenAI

import OpenAI from "openai";
import { Painite } from "@painitehq/sdk";

const painite = new Painite({ apiKey: process.env.PAINITE_API_KEY! });
const openai = new OpenAI();

const decision = await painite.protect({
  accountId: user.id,
  action: "chat_completion",
  provider: "openai",
  model: "chatgpt-5.5-latest",
  estimatedInputTokens: 5000,
  estimatedOutputTokens: 2000,
  estimatedCostUsd: 0.05,
  prompt: userPrompt,
});

if (!decision.allowed) {
  return Response.json({ error: decision.recommendedAction }, { status: 403 });
}

const result = await openai.chat.completions.create({
  model: "chatgpt-5.5-latest",
  messages: [{ role: "user", content: userPrompt }],
});

await painite.recordUsage({
  decisionRequestId: decision.requestId,
  accountId: user.id,
  action: "chat_completion",
  provider: "openai",
  model: "chatgpt-5.5-latest",
  inputTokens: result.usage?.prompt_tokens,
  outputTokens: result.usage?.completion_tokens,
  costUsd: (result.usage?.prompt_tokens ?? 0) * 2.50 / 1_000_000
         + (result.usage?.completion_tokens ?? 0) * 10 / 1_000_000,
});

With OpenRouter

import { Painite } from "@painitehq/sdk";

const painite = new Painite({ apiKey: process.env.PAINITE_API_KEY! });

const decision = await painite.protect({
  accountId: user.id,
  action: "chat_completion",
  provider: "openrouter",
  model: "anthropic/claude-opus-4-8",
  estimatedInputTokens: 5000,
  estimatedOutputTokens: 2000,
  estimatedCostUsd: 0.075,
  prompt: userPrompt,
});

if (!decision.allowed) {
  return Response.json({ error: decision.recommendedAction }, { status: 403 });
}

// Use OpenRouter's API directly or through their SDK
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "anthropic/claude-opus-4-8",
    messages: [{ role: "user", content: userPrompt }],
  }),
});

const result = await response.json();

await painite.recordUsage({
  decisionRequestId: decision.requestId,
  accountId: user.id,
  action: "chat_completion",
  provider: "openrouter",
  model: "anthropic/claude-opus-4-8",
  inputTokens: result.usage?.input_tokens ?? result.usage?.prompt_tokens,
  outputTokens: result.usage?.output_tokens ?? result.usage?.completion_tokens,
  costUsd: result.usage?.total_cost ?? 0,
});

Provider Compatibility

PainiteHQ works with any AI provider. It evaluates risk and spend — it does not call the AI provider for you.

| Provider | Supported | provider value | |---|---|---| | Anthropic (Claude) | Yes | "anthropic" | | OpenAI | Yes | "openai" | | Google Gemini | Yes | "google" | | OpenRouter | Yes | "openrouter" | | Replicate | Yes | "replicate" | | Fal.ai | Yes | "fal" | | Custom / Any | Yes | Any string |

The provider field tells PainiteHQ which pricing registry to use for cost estimation. Use the same provider SDK you already use — PainiteHQ is the decision layer, not the transport layer.


Constructor

const painite = new Painite({
  apiKey: string;          // Required. Get from dashboard.painitehq.com
  baseUrl?: string;        // Default: https://api.painitehq.com
  model?: string;          // Default model injected into every request
  timeout?: number;        // Request timeout in ms. Default: 10000
  features?: {             // Toggle gates off (all default to true)
    creditGate?: boolean;
    actionGate?: boolean;
    agentGuard?: boolean;
    sensitiveData?: boolean;
    botProtection?: boolean;
    promptAbuse?: boolean;
  };
  retry?: {
    maxRetries?: number;   // Default: 3
    initialDelayMs?: number;
    maxDelayMs?: number;
  };
  fetch?: typeof fetch;    // Custom fetch implementation
});

Methods

Core Protection

| Method | Endpoint | Description | |---|---|---| | protect(req) | POST /v1/protect | Evaluate any action against all active gates | | riskSignup(req) | POST /v1/risk/signup | Evaluate signup risk for a new account | | riskCredits(req) | POST /v1/risk/credits | Evaluate whether credits should be granted | | riskAction(req) | POST /v1/risk/action | Evaluate an expensive AI action before it runs |

Agent Lifecycle

| Method | Endpoint | Description | |---|---|---| | agentStart(req) | POST /v1/agent/start | Register an agent run with budget limits | | agentStep(req) | POST /v1/agent/step | Record a step within an active agent run | | agentEnd(req) | POST /v1/agent/end | Close an agent run with final status | | listAgentRuns(query?) | GET /v1/agent/runs | List agent runs for the workspace |

Event Tracking

| Method | Endpoint | Description | |---|---|---| | recordUsage(req) | POST /v1/events/usage | Record a usage event with cost attribution | | recordOutcome(req) | POST /v1/events/outcome | Record a conversion or payment outcome |

Health & Diagnostics

| Method | Endpoint | Description | |---|---|---| | health() | GET /v1/health | Check API health and configuration | | liveness() | GET /v1/health/liveness | Liveness probe | | readiness() | GET /v1/health/readiness | Readiness probe |

Spend Attribution

| Method | Endpoint | Description | |---|---|---| | getAttribution(query?) | GET /v1/attribution | Query spend attribution by dimension | | generateSpendLeakageReport(params?) | GET /v1/diagnostic/spend-leakage | OBSERVE mode spend leakage diagnostic | | generatePolicyRecommendations(params?) | GET /v1/attribution/policy-recommendations | Automatic policy change recommendations |

Lists & Queries

| Method | Endpoint | Description | |---|---|---| | listDecisions(query?) | GET /v1/decisions | List decision log entries | | listUsage(query?) | GET /v1/usage | List usage log entries | | listOutcomes(query?) | GET /v1/outcomes | List outcome log entries | | getAccountRisk(accountId) | GET /v1/accounts/:id/risk | Full risk history for an account |

Gate Mode Configuration

| Method | Endpoint | Description | |---|---|---| | listGateModes() | GET /v1/config/gate-modes | List current gate enforcement modes | | updateGateMode(req) | POST /v1/config/gate-modes | Update a gate's enforcement mode |

API Key Management

| Method | Endpoint | Description | |---|---|---| | createApiKey(req) | POST /v1/api-keys | Create a new API key | | listApiKeys() | GET /v1/api-keys | List all API keys | | updateApiKey(id, req) | PATCH /v1/api-keys/:id | Update an API key's name or scopes | | revokeApiKey(id) | DELETE /v1/api-keys/:id | Revoke an API key |

Enterprise

| Method | Endpoint | Description | |---|---|---| | enterpriseTrialCheck(req) | POST /v1/enterprise/trial/check | Check trial eligibility for an account | | enterpriseTrialActivate(req) | POST /v1/enterprise/trial/activate | Activate a Stripe trial | | enterpriseCreditGrant(req) | POST /v1/enterprise/credits/grant | Grant credits with risk evaluation |

Reports

| Method | Endpoint | Description | |---|---|---| | sendWeeklyReport(req) | POST /v1/reports/weekly | Send a weekly protection report via email |


What PainiteHQ Protects Against

| Abuse Pattern | Detection Method | |---|---| | Credit fraud — disposable emails, clickfarm signups, blocked countries | credit/credit-gate.ts | | Prompt injection & jailbreak — role-play escapes, system prompt extraction, base64/rot13 obfuscation | prompt/abuse-scanner.ts, jailbreak/ (14 files) | | Multi-account abuse — 10 Chrome profiles on one machine, subnet bursts, ASN concentration, WebRTC leakage | infringement/ (13 files) | | Agent budget runaway — retry loops, model switching, tool-call spikes, runtime budget acceleration | agent/agent-lifecycle.ts | | Sensitive data leakage — credit cards (Luhn), API keys, PEM private keys, PII to AI providers | data/sensitive-data-scanner.ts | | Bot & automation — headless browsers, abusive AI crawlers (GPTBot, ClaudeBot, CCBot) | traffic/bot-intelligence.ts | | Runaway cost — daily/monthly budget enforcement, cost spike detection, budget acceleration | budget/budget-controls.ts, cost/velocity/ | | Signup abuse — suspicious velocity, incomplete profiles, VPN at signup | detection/signup-protection.ts |


Response Shape

Every protect(), riskSignup(), riskCredits(), and riskAction() call returns:

{
  allowed: boolean;
  decision: "allow" | "deny" | "throttle" | "require_verification" | "cap_usage";
  riskScore: number;          // 0-100
  confidence: "low" | "medium" | "high";
  signals: string[];          // Active signals that influenced the decision
  reasonCodes: string[];      // Why the decision was made
  matchedPolicies: string[];
  recommendedAction: string;
  estimatedCostUsd: number | null;
  remainingBudget: number | null;
  requestId: string;
  timestamp: string;
  enforcementMode?: "enforce" | "observe" | "dry_run";
}

The customer decides what to do with the response — allow, block, throttle, or require verification. PainiteHQ returns the intelligence; enforcement lives in your code.


OBSERVE vs ENFORCE Mode

Every gate operates in OBSERVE or ENFORCE mode. During onboarding, run in OBSERVE mode to measure what would have been blocked without affecting production traffic. Generate a spend leakage diagnostic to see the dollar amount of AI spend that would have been protected:

const leakage = await painite.generateSpendLeakageReport({ days: 30 });
console.log(`Estimated wasted spend: $${leakage.totalEstimatedLeakageUsd}`);

Switch gates to ENFORCE individually when you're ready:

await painite.updateGateMode({ gateName: "protect", mode: "enforce" });

Error Handling

The SDK throws PainiteError on failed requests with a consistent shape:

import { Painite, PainiteError } from "@painitehq/sdk";

try {
  const decision = await painite.protect({ ... });
} catch (err) {
  if (err instanceof PainiteError) {
    console.error(err.status, err.code, err.message, err.requestId);
  }
}

Error codes: unauthorized, invalid_response, timeout, request_failed, max_retries_exceeded, or passthrough HTTP status codes.


Architecture

Your app → PainiteHQ SDK → PainiteHQ API → Decision → Your app → AI Provider
  1. Call protect() before your AI call with estimated token counts and cost
  2. PainiteHQ evaluates the request against 25+ detection modules, policies, and account history
  3. A decision is returned: allow, deny, throttle, require verification, or cap usage
  4. If allowed, call your AI provider
  5. Call recordUsage() after with real token counts and cost for attribution

PainiteHQ does not call your AI provider. It decides whether the call should happen.


Retries & Reliability

  • Automatic retries on 408, 429, 500, 502, 503, 504
  • Respects Retry-After header (delta-seconds and HTTP-date)
  • Exponential backoff with jitter
  • Default: 3 retries, configurable
  • Timeout default: 10 seconds

Zero Runtime Dependencies

The SDK has no dependencies outside the Web API (fetch, crypto.randomUUID, AbortController). Works in Node.js 18+, Bun, Deno, Cloudflare Workers, Vercel Edge, and any runtime with Web API support.


Dashboard

Get your API key at painite.site. API keys are available to paid customers only.