Skip to content

paygentic/wrap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Wrap SDK

Wrap Claude agent queries with automatic payment tracking and usage metering.

Paygentic Platform: Production | Sandbox

Quick Start

import { createWrapClient } from "@paygentic/wrap";

const client = createWrapClient();
const plan = await client.plans.init();
const query = plan.wrap("customer_123");

// wrapped query supports all claude agent sdk options
const response = await query({
  model: "claude-sonnet-4-20250514",
  prompt: "Hello, world!",
});

for await (const message of response) {
  // Process messages as usual
  // Usage is automatically tracked on completion
}

Installation

npm install @paygentic/wrap

CLI Setup

The package includes an interactive setup command that creates the necessary Paygentic resources (product, billable metric, price, and plan) for your agent:

npx @paygentic/wrap setup

What It Does

The setup wizard will:

  1. Prompt for configuration:

    • Environment (sandbox or prod)
    • API Key (from Paygentic Platform)
    • Merchant ID (from Organization Settings)
    • Agent name (used for product naming)
    • Payment type (instant or post-paid)
    • Max expected usage per query (USD)
    • Margin percentage (markup on usage costs)
  2. Create Paygentic resources:

    • Product for your agent
    • Billable metric for tracking usage
    • Price with your configured margin
    • Plan linking everything together
  3. Output environment variables to add to your .env file

Example Session

=== Paygentic Agent Billing Setup ===

Environment (sandbox/prod) [sandbox]: sandbox
Get your API key from: https://platform.sandbox.paygentic.io/merchant/developer/apikeys
API Key: pk_sandbox_xxxxx
Merchant ID: mer_xxxxx
Agent Name: My AI Assistant
Payment Type (instant/post-paid): instant
Max expected usage per query (USD) [2.00]: 2.00
Margin percentage [10]: 15

--- Creating Paygentic resources ---

Creating product...
Creating billable metric...
Creating price...
Creating plan...

=== Setup complete! ===

Add these to your .env file:

PAYGENTIC_WRAP_ENV=sandbox
PAYGENTIC_WRAP_API_KEY=pk_sandbox_xxxxx
PAYGENTIC_WRAP_MERCHANT_ID=mer_xxxxx
PAYGENTIC_WRAP_PRODUCT_ID=prod_xxxxx
PAYGENTIC_WRAP_PLAN_ID=plan_xxxxx
PAYGENTIC_WRAP_PRICE_ID=price_xxxxx
PAYGENTIC_WRAP_BILLABLE_METRIC_ID=bm_xxxxx

Environment Variables

The SDK reads these environment variables as defaults:

Variable Description Required
PAYGENTIC_WRAP_API_KEY Your Paygentic API key Yes (or pass apiKey option)
PAYGENTIC_WRAP_ENV Environment: prod or sandbox No (defaults to prod)
PAYGENTIC_WRAP_MERCHANT_ID Your merchant ID For setup script
PAYGENTIC_WRAP_PLAN_ID Your plan ID For your app
PAYGENTIC_WRAP_PRICE_ID Your price ID For your app
PAYGENTIC_WRAP_PRODUCT_ID Your product ID For reference
PAYGENTIC_WRAP_BILLABLE_METRIC_ID Your billable metric ID For reference

Run npx @paygentic/wrap setup to create these resources and get the environment variables.

How It Works

When you call wrap, the SDK:

  1. Verifies the customer has an active subscription
  2. Sets a safe budget limit (90% of maxPrice or maxPrice - $0.10)
  3. Reserves funds for instant payments
  4. Tracks usage and creates billing/payment events on completion
  5. Handles overages by splitting into multiple events if needed

Pricing Models

Dynamic Pricing

Only pass through usage cost to end user:

// Price config: maxPrice >= $0.20
const plan = await client.plans.init({
  planId: "plan_abc123",
  priceId: "price_dynamic",
});

Percentage Pricing

Apply a markup to usage cost:

// Price config: percentage, minCharge, maxCharge (maxCharge >= $0.20)
// Plan and price id default to PAYGENTIC_WRAP_PLAN_ID resp PAYGENTIC_WRAP_PRICE_ID
const plan = await client.plans.init({
  planId: "plan_abc123",
  priceId: "price_percentage",
});

Subscriptions

Create a Customer and Subscription

const result = await client.subscriptions.create({
  customer: {
    name: "Example Inc",
    email: "billing@example.com",
    address: {
      line1: "123 Main St",
      city: "San Francisco",
      state: "CA",
      postalCode: "94105",
      country: "US",
    },
  },
  minWalletAmount: "20.00",
  redirectUrls: {
    onSuccess: "https://example.com/success",
    onFailure: "https://example.com/failure",
  },
});

const { customerId, subscriptionDetail } = result;

Get Customer Portal URL

const portal = await client.subscriptions.portal(subscriptionId);
// portal.url - redirect customers here to manage their subscription
// portal.expiresAt - URL expiration time

Find Customer by Email

const customerId = await client.subscriptions.findCustomer("billing@example.com");

Payment Terms

  • Pre-paid (default): Creates an entitlement before the query runs
  • In-arrears: Records usage after the query completes

Error Handling

Insufficient Funds

When a customer doesn't have enough balance to process a query, the SDK throws an InsufficientFundsError with a portal URL so they can top up:

import { createWrapClient, InsufficientFundsError } from "@paygentic/wrap";

const client = createWrapClient();
const plan = await client.plans.init();
const query = plan.wrap("customer_123");

try {
  const response = await query({ model: "claude-sonnet-4-20250514", prompt: "Hello" });
  for await (const message of response) {
    // Process messages
  }
} catch (error) {
  if (error instanceof InsufficientFundsError) {
    console.log("Please top up your balance:", error.portalUrl);
    console.log("Link expires at:", error.portalExpiresAt);
    console.log("Subscription ID:", error.subscriptionId);
    // Redirect customer to error.portalUrl to add funds
  } else {
    throw error;
  }
}

The InsufficientFundsError includes:

  • portalUrl - URL where the customer can add funds to their wallet
  • portalExpiresAt - When the portal URL expires (ISO timestamp)
  • subscriptionId - The subscription that needs funding

Query Stopped Due to Budget Limit

The SDK automatically sets a budget limit based on your pricing config to protect against runaway costs. When a query exceeds this limit, the agent stops and the result message has subtype: "error_max_budget_usd".

You can resume the query by passing the session_id in the options.resume field:

let sessionId: string | undefined;

const response = await query({ model: "claude-sonnet-4-20250514", prompt: "Complex task" });

for await (const message of response) {
  if (message.type === "result") {
    sessionId = message.session_id;

    if (message.subtype === "error_max_budget_usd") {
      console.log("Query stopped due to budget limit");
      console.log("Cost incurred:", message.total_cost_usd);
    }
  }
}

// Resume the query if it stopped due to budget limit
if (sessionId) {
  const continuation = await query({
    model: "claude-sonnet-4-20250514",
    prompt: "Please continue",
    options: { resume: sessionId },
  });

  for await (const message of continuation) {
    // Billing is handled automatically for the continuation
  }
}

About

Use claude agent sdk with paygentic

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •