PINTI Docs

SDK — @pinti/guard

Policy enforcement + receipt audit for AI agent payments. Works with any payment rail — Stripe, MoonPay, crypto, or custom.

Installation

npm install @pinti/guard

Quick Start

Setup
import { PintiGuard } from "@pinti/guard";

const pinti = new PintiGuard({
  apiKey: process.env.PINTI_API_KEY!,
  baseUrl: "https://pinti.ai",  // optional, defaults to this
  handleId: "clxyz...",          // optional default handle
});

Tip

Create an SDK Guard handle in Settings. This tells PINTI your agent handles payments directly and receipts should be expected.

authorize()

Evaluate a spend intent against your workspace policies. Returns the decision and a SAT (Spend Authorization Token) on ALLOW. Throws SpendDeniedError on DENY and ApprovalRequiredError on REQUIRE_APPROVAL.

Authorize a spend
const auth = await pinti.authorize({
  amountMinor: 5000,     // $50.00 in cents
  currency: "usd",
  merchant: "AWS",
  agentId: "agent-1",
  category: "cloud",
  reason: "EC2 instance for data processing",
});

// auth.decision === "ALLOW"
// auth.sat === "eyJ..."   — proof of authorization
// auth.spendRequestId === "cm..."

submitReceipt()

After your payment executes (on any rail), submit a receipt to PINTI. PINTI compares the actual payment against the SAT claims and flags anomalies.

Submit a receipt
await pinti.submitReceipt({
  spendRequestId: auth.spendRequestId,
  sat: auth.sat,
  railId: "stripe",
  providerTransactionId: "ch_xxx",
  actualAmountMinor: 5000,
  actualCurrency: "usd",
});
// Returns: { receiptId, anomalyFlags, recorded }

Note

Receipts are idempotent. Submitting the same SAT twice returns the same receiptId — never a 409 error.

guardedAction()

The recommended pattern. Wraps authorize → execute → receipt into a single call. If authorization fails, your payment callback is never called.

Stripe Example

guardedAction with Stripe
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_KEY!);

const result = await pinti.guardedAction(
  {
    amountMinor: 5000,
    currency: "usd",
    merchant: "AWS",
    agentId: "agent-1",
    category: "cloud",
    reason: "EC2 instance",
  },
  async (auth) => {
    const charge = await stripe.charges.create({
      amount: 5000,
      currency: "usd",
      description: "EC2 instance via PINTI",
    });
    return {
      railId: "stripe",
      transactionId: charge.id,
      actualAmountMinor: charge.amount,
      actualCurrency: charge.currency,
    };
  }
);

// result.decision === "ALLOW"
// result.receipt.anomalyFlags === []
// result.receipt.receiptId === "cl..."

MoonPay Example

guardedAction with MoonPay
const result = await pinti.guardedAction(
  {
    amountMinor: 10000,  // $100.00
    currency: "usd",
    merchant: "moonpay:onramp",
    agentId: "agent-1",
    category: "crypto",
    reason: "Buy ETH for gas fees",
  },
  async (auth) => {
    const tx = await moonpay.createTransaction({
      baseCurrencyAmount: 100,
      baseCurrencyCode: "usd",
      currencyCode: "eth",
    });
    return {
      railId: "moonpay",
      transactionId: tx.id,
      actualAmountMinor: 10000,
      actualCurrency: "usd",
      actualNetwork: "moonpay",
    };
  }
);

Custom Rail Example

guardedAction with any custom payment rail
const result = await pinti.guardedAction(
  {
    amountMinor: 2500,
    currency: "usd",
    merchant: "internal-credits",
    agentId: "agent-1",
    category: "credits",
    reason: "Deduct credits for API usage",
  },
  async (auth) => {
    const txId = await myCustomBilling.deductCredits({
      userId: "user-123",
      amount: 25.00,
    });
    return {
      railId: "custom:internal-billing",
      transactionId: txId,
      actualAmountMinor: 2500,
      actualCurrency: "usd",
    };
  }
);

Error Handling

Catching denials and approval requests
import { SpendDeniedError, ApprovalRequiredError } from "@pinti/guard";

try {
  const auth = await pinti.authorize({
    amountMinor: 50000, currency: "usd",
    merchant: "expensive-service", agentId: "agent-1",
    category: "saas", reason: "Enterprise plan",
  });
} catch (err) {
  if (err instanceof SpendDeniedError) {
    console.log("Denied:", err.decisionReason);
    // e.g. "EXCEEDS_DAILY_LIMIT", "BLOCKED_MERCHANT"
  }
  if (err instanceof ApprovalRequiredError) {
    console.log("Needs approval:", err.spendRequestId);
    // Wait for human to approve in PINTI dashboard
  }
}
Error ClassWhenProperties
SpendDeniedErrorPolicy denies the spenddecisionReason, spendRequestId
ApprovalRequiredErrorAmount exceeds approval thresholddecisionReason, spendRequestId
PintiApiErrorHTTP error (401, 429, 500)statusCode

Anomaly Detection

When you submit a receipt, PINTI compares actual payment values against the SAT claims. Mismatches are flagged as anomalies:

FlagMeaning
AMOUNT_MISMATCHActual amount differs from authorized amount
CURRENCY_MISMATCHActual currency differs from authorized currency
MERCHANT_MISMATCHActual merchant differs from authorized merchant
STALE_POLICYPolicy was modified between authorization and receipt
MISSING_RECEIPTNo receipt uploaded within expected window (SDK bypass)

How SDK Guard Works

Note

SDK Guard is advisory enforcement + audit. Your agent holds the payment credentials and executes payments directly. PINTI enforces policy at the evaluate step (server-side) and detects anomalies by comparing receipts to SAT claims. If your agent bypasses the SDK entirely, PINTI detects the missing receipt.