Skip to content

Your First Envelope

A spend envelope is a signed, bounded authorization for an AI agent to spend money. Think of it as a signed cheque with strict limits — the agent can only cash it within the bounds you define.

Full envelope example

import { createSpendEnvelope } from '@pqsafe/agent-pay'
const envelope = createSpendEnvelope({
// --- Identity ---
agentId: 'procurement-agent-v2', // Which agent this is issued to
issuedBy: 'ops-team@example.com', // Who authorized this envelope
// --- Amount bounds ---
maxAmount: 100, // Hard cap: agent cannot spend more than $100
currency: 'USD', // ISO 4217 currency code
// --- Time bounds ---
validFrom: new Date(), // Optional: not valid before this time
validUntil: new Date(Date.now() + 86_400_000), // Expires in 24 hours
// --- Destination constraints ---
allowedRecipients: [ // Allowlist — agent can only pay these
'anthropic.com',
'openai.com',
],
// --- Rail constraints ---
allowedRails: ['airwallex', 'stripe'], // Which payment rails may be used
// --- Approval gate ---
requireApproval: false, // Set true to require human approval before dispatch
approvalThreshold: 50, // Auto-approve below $50, require human above
// --- Memo ---
memo: 'AI model API costs — Q3 2026',
})

Field reference

FieldTypeRequiredDescription
agentIdstringYesIdentifier for the agent instance. Appears in ledger entries.
maxAmountnumberYesHard cap on spending. SDK rejects any payment request exceeding this.
currencystringYesISO 4217 code (USD, EUR, GBP, USDC).
validUntilDateYesEnvelope expires at this time. Expired envelopes are rejected at signature verification.
allowedRecipientsstring[]YesDomain or address allowlist. Must match recipient in executeAgentPayment().
allowedRailsRailName[]YesWhich payment rails may be used.
requireApprovalbooleanNoIf true, payment pauses until a human approves via Telegram/webhook.
approvalThresholdnumberNoAuto-approve below this amount; require approval above it.
memostringNoHuman-readable purpose. Appears in ledger and bank reference.
validFromDateNoEnvelope not valid before this time. Useful for deferred authorizations.
issuedBystringNoIssuer identifier. Useful for audit trails in multi-team environments.

What happens at signing

When you call createSignedEnvelope(envelope, secretKey):

  1. The envelope is serialized to Canonical JSON (deterministic key ordering, no whitespace)
  2. An ML-DSA-65 signature is computed over the canonical bytes
  3. The signature is bound to this exact envelope — any modification invalidates it
  4. A unique envelopeId is derived from the hash of the canonical JSON
import { createSignedEnvelope, verifyEnvelope } from '@pqsafe/agent-pay'
const signed = createSignedEnvelope(envelope, secretKey)
// Verify (useful for debugging or at the receiving end)
const isValid = verifyEnvelope(signed, publicKey)
console.log('Signature valid:', isValid) // true

Envelope lifecycle

Created → Signed → [Approval gate if required] → Dispatched → Settled → Archived in ledger
Human approves via Telegram

Envelopes can only be used once per payment (no replay). The ledger records every attempt.

Next steps