paegents
v2.9.1
Published
Official TypeScript/JavaScript SDK for Paegents - Payment infrastructure for AI agents with Service Catalog and Usage Escrow
Maintainers
Readme
Paegents TypeScript SDK
Official TypeScript/JavaScript SDK for integrating Paegents agent payments.
Installation
npm install paegents
# or
yarn add paegentsStablecoin helpers are opt-in and require compatible peers:
npm install viem x402@^0.1.2Initialize
import { PaegentsSDK } from 'paegents'
const sdk = new PaegentsSDK({
apiUrl: process.env.PAEGENTS_API_URL || 'https://api.paegents.com',
agentId: process.env.PAEGENTS_AGENT_ID!,
apiKey: process.env.PAEGENTS_API_KEY!,
ownerJwt: process.env.OWNER_JWT, // optional: owner-only endpoints
})Agent runtime auth is apiKey + agentId. ownerJwt is only for owner-scoped routes such as policy management, dashboard setup, or owner-managed webhooks.
Security Model
All signing happens locally — private keys never leave your environment. Escrow funds are held by an on-chain smart contract, not by Paegents. Wallet addresses are screened for sanctions compliance before activation.
Keep API keys and private keys in environment variables. Never commit them to source control.
AP2 Quick Start
import { buildCardPaymentMethod } from 'paegents'
const intent = await sdk.createAp2IntentMandate({
policy: { max_amount: { value: 5000 }, currency: 'usd' },
metadata: { purpose: 'compute credits' },
})
const cart = await sdk.createAp2CartMandate({
intentMandateId: intent.id,
cart: { total: 2500, currency: 'usd' },
})
const payment = await sdk.ap2Pay({
intentMandateId: intent.id,
cartMandateId: cart.id,
paymentMethod: buildCardPaymentMethod({ provider: 'stripe' }),
})
if ('approvalRequired' in payment && payment.approvalRequired) {
console.log('Approval required:', payment.requestId)
} else {
console.log('Payment status:', payment.status)
}Bilateral Escrow Usage Agreements
Bilateral escrow is a two-step process: create the agreement, then activate the on-chain escrow.
Activation is required. Without completing activation, the agreement stays at
accepted/activationStatus: 'ready'. The metered proxy will reject requests and no funds are deposited on-chain.
import {
signBuyerActivationIntent,
signInfraFeePermit,
buildPriorAllowanceFundingAuthorization,
} from 'paegents/escrow'
// Step 1: Create the agreement (auto-accepted for self-service)
const agreement = await sdk.createUsageAgreement({
sellerAgentId: 'seller-agent-123',
serviceId: 'svc_abc123',
quantity: 1000,
unit: 'api_calls',
pricePerUnitCents: 10,
buyerWalletAddress: process.env.AGENT_WALLET_ADDRESS!,
})
// Step 2: Fetch the activation package (terms, nonces, funding options, infra fee)
const pkg = await sdk.getActivationPackage(agreement.agreementId)
// Step 3a: Sign the buyer activation intent locally (EIP-712)
const intent = {
agreementKey: pkg.agreementKey,
platformAgreementIdHash: pkg.platformAgreementIdHash,
buyer: pkg.terms.buyer,
seller: pkg.terms.seller,
token: pkg.terms.token,
quantity: BigInt(pkg.terms.quantity),
pricePerUnit: BigInt(pkg.terms.pricePerUnitAtomic),
depositAmount: BigInt(pkg.terms.depositAmountAtomic),
challengeWindowSeconds: BigInt(pkg.terms.challengeWindowSeconds),
serviceHash: pkg.terms.serviceHash,
termsHash: pkg.terms.termsHash,
expiry: pkg.terms.expiry,
nonce: pkg.nonces.buyerActivationNonce,
salt: pkg.terms.salt,
}
const sigResult = await signBuyerActivationIntent({
buyerPrivateKey: process.env.AGENT_PRIVATE_KEY!,
escrowContractAddress: pkg.funding.spender,
chainId: 8453, // Base mainnet
intent,
})
// Step 3b: Build funding authorization (prior USDC allowance)
const funding = buildPriorAllowanceFundingAuthorization({
amount: BigInt(pkg.terms.depositAmountAtomic),
})
// Step 3c: Sign the separate infra fee permit when the package says a fee is pending
const permitDeadline = Math.floor(Date.now() / 1000) + 600
const buyerUsdcPermitNonce = Number(process.env.BUYER_USDC_PERMIT_NONCE) // read USDC.nonces(buyer)
const infraFeePermit = pkg.infraFee && !pkg.infraFee.waived && pkg.infraFee.status !== 'paid'
? await signInfraFeePermit({
buyerPrivateKey: process.env.AGENT_PRIVATE_KEY!,
spender: pkg.infraFee.spender,
value: BigInt(pkg.infraFee.amountAtomic),
nonce: buyerUsdcPermitNonce,
deadline: permitDeadline,
chainId: 8453, // Base mainnet
usdcAddress: pkg.infraFee.tokenAddress,
})
: undefined
// Step 4: Submit activation to the server with both authorizations
await sdk.activateEscrow(agreement.agreementId, {
buyerIntent: intent,
buyerSignature: sigResult.signature,
fundingAuthorization: funding,
buyerInfraFeePermitSignature: infraFeePermit?.signature,
buyerInfraFeePermitDeadline: infraFeePermit ? permitDeadline : undefined,
})
// Step 5: Poll until active (typically 10-30 seconds on Base)
let current = await sdk.getUsageAgreement(agreement.agreementId)
while (current.status !== 'active') {
await new Promise((r) => setTimeout(r, 3000))
current = await sdk.getUsageAgreement(agreement.agreementId)
}Optional: Metered Client
const client = sdk.createMeteredClient(agreement.agreementId)
const result = await client.post('/generate', {
prompt: 'hello world',
max_tokens: 150,
})
const usage = await client.getUsageStatus()
console.log(usage.units_used, usage.units_remaining)Policies and Approvals
Owner JWT is required for these endpoints.
const ownerJwt = process.env.OWNER_JWT!
await sdk.updateAgentPolicies(
{
approvals: { threshold_cents: 2000 },
rails: { allowed: ['card', 'stablecoin'] },
spending: { daily_limit_cents: 10000 },
},
'agent-123',
ownerJwt,
)
const { approvals } = await sdk.listApprovals({ status: 'pending', agentId: 'agent-123' }, ownerJwt)
if (approvals.length > 0) {
await sdk.approveApproval(approvals[0].id, 'agent-123', ownerJwt)
}Webhooks
const ownerJwt = process.env.OWNER_JWT!
const webhook = await sdk.createWebhook(
{
url: 'https://example.com/webhooks',
eventTypes: ['payment.*', 'agreement.*'],
},
'agent-123',
ownerJwt,
)
console.log(webhook.id)Verify Webhook Signatures
import { verifyWebhookSignature } from 'paegents'
verifyWebhookSignature(signatureHeader, rawBody, process.env.PAEGENTS_WEBHOOK_SECRET!)Error Handling
import { ApiError, PolicyDeniedError } from 'paegents'
try {
await sdk.ap2Pay({
intentMandateId: 'intent_123',
cartMandateId: 'cart_123',
paymentMethod: buildCardPaymentMethod(),
})
} catch (err) {
if (err instanceof PolicyDeniedError) {
console.error('Policy denied:', err.message)
} else if (err instanceof ApiError) {
console.error('API error:', err.status, err.message)
} else {
console.error('Unexpected error:', err)
}
}Notes
- Prefer SDK methods over manual HTTP calls.
- Keep API keys and JWTs in environment variables.
- Use idempotency keys on critical write operations.
- Bilateral escrow settlement auto-pays on finalization. Buyer and seller do not need a second payout signature after the agreement is already funded and metered.
- The platform fee is upfront-only in the current live model. Settlement should not perform a second per-call fee pull.
Support
- Docs: https://docs.paegents.com
- API docs: https://docs.paegents.com/api
- Support: [email protected]
License
MIT
