PINTI Docs

Execution Modes

PINTI supports three execution modes. Choose based on your enforcement needs and how your agent handles payments.

Overview

ModeHandle TypeEnforcementBest For
SDK GuardsdkAdvisory + auditAgents with existing payment credentials
External Executorexternal_executorHard (HMAC-signed)Custom payment backends
Managedmanaged_stripe / managed_moonpayHard (PINTI owns execution)Zero-config enterprise (coming soon)

Warning

Hard enforcement requires that payment credentials are not exposed to the agent runtime. If your agent has direct access to raw API keys or card numbers, enforcement is advisory only. For hard enforcement, use External Executor or Managed mode.

SDK Guard Mode

Your agent holds its own payment credentials and executes payments directly. PINTI provides server-side policy enforcement (at the evaluate step) and anomaly detection via receipt comparison.

  1. Install @pinti/guard in your agent
  2. Call pinti.authorize() before each payment — PINTI evaluates policy and returns a SAT
  3. Execute the payment on any rail (Stripe, MoonPay, EVM, etc.)
  4. Call pinti.submitReceipt() after payment
  5. PINTI compares receipt vs SAT → flags anomalies

Note

If your agent bypasses the SDK and pays without calling authorize(), PINTI detects the missing receipt after the expected window passes. See the SDK documentation for full usage examples.

External Executor Mode

  1. Create a payment handle in Settings. You provide your executor backend URL and an auth token. These are encrypted at rest and never shown again.
  2. Agent receives only the handle ID — a short opaque string. No URLs, no tokens, no credentials.
  3. Agent calls POST /api/v1/spend/evaluate — PINTI evaluates policy and returns a SAT.
  4. Agent calls POST /api/v1/payment/execute with the handle ID, SAT, and payment parameters.
  5. PINTI verifies the SAT, atomically consumes it, then calls your executor backend server-to-server with a signed request.
  6. Your backend executes the actual payment and returns the result. Zero credential exposure.

Note

Your executor endpoint must not be reachable by the agent runtime. Deploy your executor in a network segment the agent cannot access.

API: Execute Payment

POST /api/v1/payment/execute
// Request
POST /api/v1/payment/execute
x-api-key: pinti_xxxxxxxx_xxxxxxxxxxxxxxxxxx

{
  "paymentHandleId": "clxyz...",
  "sat": "eyJ2ZXJzaW9uIjox...",
  "params": {
    "amountMinor": 5000,
    "currency": "usd",
    "description": "Monthly API credits"
  }
}

// Success Response (200)
{
  "executed": true,
  "spendRequestId": "cm...",
  "transaction": {
    "id": "tx_xxxxxxxxxx",
    "status": "completed"
  }
}

// Error Responses
// 400 — SDK mode SAT (use /spend/receipt instead)
// 403 — Handle not found, inactive, or mode mismatch
// 409 — SAT already consumed or expired
// 502 — Executor returned error (SAT stays consumed)
// 504 — Executor unreachable (retryable with same SAT JTI)

Verifying PINTI Requests

When PINTI calls your executor backend, it includes two headers for request authentication:

  • X-Pinti-Timestamp — Unix timestamp of the request
  • X-Pinti-Signature — HMAC-SHA256 of timestamp.body using your signing secret

Your executor should:

  1. Reject requests where the timestamp drifts more than 5 minutes
  2. Recompute the HMAC and compare with the signature header
  3. Use the satJti field as an idempotency key

Warning

MUST: Use satJti as idempotency key. If PINTI crashes after your executor succeeds, PINTI will retry with the same satJti. Without idempotency on your side, this will cause double charges. This is not optional. This is required for enforcement integrity.
Executor verification example (Node.js)
import crypto from "crypto";

function verifyPintiRequest(req, signingSecret) {
  const timestamp = req.headers["x-pinti-timestamp"];
  const signature = req.headers["x-pinti-signature"];
  const body = JSON.stringify(req.body);

  // 1. Check timestamp drift (±5 minutes)
  const drift = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (drift > 300) return false;

  // 2. Verify HMAC
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(timestamp + "." + body)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
}

Managed Mode (Coming Soon)

PINTI owns the execution. You connect your provider (e.g. Stripe restricted key), and PINTI executes payments directly. The agent sees only the handle ID and SAT. Zero credential exposure, zero executor backend to build.


Enforcement Guarantees

ConditionResult
No SATNo payment execution
Consumed SAT409 — cannot replay
Expired SAT400 — rejected
Amount/currency mismatch403 — blocked
SDK mode SAT on /execute400 — wrong endpoint
Mode ↔ handle type mismatch403 — blocked
Missing receipt (SDK mode)Anomaly flagged in dashboard
Agent has raw credentialsAdvisory — use SDK Guard for audit trail
Agent only has handle IDHard enforce — must go through PINTI

Tip

PINTI enforces payments by owning the execution surface. If you give your agent direct payment credentials, use SDK Guard mode for advisory enforcement and anomaly detection.