@painitehq/sdk
v1.1.4
Published
Decision API for AI abuse prevention.
Maintainers
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/sdkQuickstart
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- Call
protect()before your AI call with estimated token counts and cost - PainiteHQ evaluates the request against 25+ detection modules, policies, and account history
- A decision is returned: allow, deny, throttle, require verification, or cap usage
- If allowed, call your AI provider
- 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-Afterheader (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.
