PQSafe AgentPay Python SDK — post-quantum safe payments for AI agents
Project description
pqsafe-agent-pay
Python SDK for PQSafe AgentPay — post-quantum safe payments for AI agents.
Uses ML-DSA-65 (NIST FIPS 204) for signing. Works with LangChain, CrewAI, AutoGen, and custom Python agents.
Install
pip install pqsafe-agent-pay
Or for local development:
git clone https://github.com/PQSafe/pqsafe
cd pqsafe/python-sdk
pip install -e ".[dev]"
30-second example
from pqsafe import create_envelope, generate_keypair, pay, sign_envelope
# 1. Generate an ML-DSA-65 key pair (issuer/wallet owner)
keypair = generate_keypair()
# 2. Create a SpendEnvelope authorizing an AI agent to pay up to $10 USD
envelope = create_envelope(
issuer="pq1" + "a" * 40, # wallet owner's PQSafe address
agent="my-ai-agent-v1",
max_amount=10.00,
currency="USD",
allowed_recipients=["38873dbc-abfa-4ab5-be25-050496d4a0c3"],
ttl_seconds=3600,
)
# 3. Sign the envelope
signed = sign_envelope(envelope, keypair)
# 4. Agent pays (dry_run=True skips HTTP — remove for live payments)
result = pay(
signed,
recipient="38873dbc-abfa-4ab5-be25-050496d4a0c3",
amount=5.00,
memo="supplier invoice #42",
dry_run=True,
)
print(result.tx_id) # "dry-run-no-http"
print(result.status) # "dry_run"
Set PQSAFE_API_KEY in your environment and remove dry_run=True for live calls.
API reference
generate_keypair() -> KeyPair
Generate an ML-DSA-65 key pair. Returns a KeyPair with .public_key and .secret_key as raw bytes. Use .public_key_hex() and .secret_key_hex() for hex-encoded strings.
create_envelope(...) -> SpendEnvelope
Build a new unsigned SpendEnvelope. Parameters:
| Parameter | Type | Description |
|---|---|---|
issuer |
str |
PQSafe address (pq1 + 40 hex chars) |
agent |
str |
Agent identifier (1-128 chars) |
max_amount |
float |
Maximum spend allowed |
currency |
str |
ISO 4217 currency code (e.g. "USD") |
allowed_recipients |
list[str] |
Allowlist of recipient addresses |
starts_in_seconds |
int |
Delay before activation (default 0) |
ttl_seconds |
int |
Validity window in seconds (default 3600) |
rail |
str | None |
Optional rail constraint |
sign_envelope(envelope, keypair) -> SignedEnvelope
Sign a SpendEnvelope with the issuer's ML-DSA-65 key pair.
verify_envelope(signed, public_key=None) -> SpendEnvelope
Verify a SignedEnvelope. Raises ValueError on invalid signature or expired envelope.
pay(signed_envelope, request=None, *, recipient=None, amount=None, memo=None, api_key=None, base_url='https://api.pqsafe.xyz', dry_run=False) -> PaymentResult
Verify the envelope and POST to /v1/pay. Accepts a PaymentRequest, plain dict, or keyword args.
AP2 adapter (v0.1.1)
Convert between AP2 mandates and PQSafe SpendEnvelopes:
from pqsafe.adapters import ap2_mandate_to_spend_envelope, spend_envelope_to_ap2_mandate, IntentMandate
# IntentMandate → SpendEnvelope
mandate = IntentMandate(
mandateId="m-001",
merchantId="merchant-xyz",
description="Purchase intent",
maxAmount=100.0,
currency="USD",
expiresAt="2026-12-31T23:59:59Z",
agentId="my-agent-v1",
issuerAddress="pq1" + "a" * 40,
)
envelope = ap2_mandate_to_spend_envelope(mandate, issuer_address="pq1" + "a" * 40, ttl_seconds=3600)
# SpendEnvelope → CartMandate
cart = spend_envelope_to_ap2_mandate(envelope, "cart")
# Verify an AP2 mandate with a PQ signature
from pqsafe.adapters import verify_ap2_with_pq_wrapper
valid = verify_ap2_with_pq_wrapper(mandate, pq_sig_bytes, pq_public_key_bytes)
ACP adapter (v0.1.1)
Convert between Stripe ACP Shared Payment Tokens and SpendEnvelopes:
from pqsafe.adapters import acp_token_to_spend_envelope, spend_envelope_to_acp_token, SharedPaymentToken
spt = SharedPaymentToken(...) # from Stripe API
envelope = acp_token_to_spend_envelope(spt, issuer_address="pq1" + "a" * 40)
# Back to SPT creation params (for POST /v1/shared_payment_tokens)
params = spend_envelope_to_acp_token(envelope, payment_method_id="pm_123")
Zero-decimal currencies (JPY, KRW, etc.) are handled automatically — no manual division by 100.
Sprint 2: Spend Policy (v0.1.1)
Three policy modes for SpendEnvelopes:
from pqsafe.sprint2 import SingleUsePolicy, PerTxCapPolicy, CumulativeCapPolicy
from pqsafe.sprint2 import validate_spend_policy, assert_policy_consistency
policy = validate_spend_policy({"mode": "per_tx_cap", "perTxLimit": 25.0})
assert_policy_consistency(policy, max_amount=100.0) # validates perTxLimit <= maxAmount
Revocation and issuer hierarchy stubs are included (Sprint 3 implementation: May 19 – Jun 8).
Links
- Handbook: https://pqsafe.xyz/handbook
- TypeScript SDK: https://github.com/PQSafe/pqsafe/tree/main/agent-pay
- LangChain plugin:
pip install langchain-pqsafe - CrewAI plugin:
pip install crewai-pqsafe
Cryptographic backend
The SDK uses ML-DSA-65 (NIST FIPS 204) via pqcrypto>=0.4.0. If pqcrypto cannot be installed, a classical Ed25519 fallback is used (clearly marked in the code with TODO comments). The fallback is not post-quantum secure and must not be used in production.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pqsafe_agent_pay-0.1.0.tar.gz.
File metadata
- Download URL: pqsafe_agent_pay-0.1.0.tar.gz
- Upload date:
- Size: 48.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98458a7a59d5ed3538db334beb8fcc08ca11e805e46abd6da72f126eea16b401
|
|
| MD5 |
99cb198dfe8914416d34dcd62482b89d
|
|
| BLAKE2b-256 |
62ec0a42bcdd414b7bfe66a80ee00d1b91bb7d5412e35e9265f96463df88cc38
|
File details
Details for the file pqsafe_agent_pay-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pqsafe_agent_pay-0.1.0-py3-none-any.whl
- Upload date:
- Size: 41.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
626ab12682bbdf56d5b959121f0ac2399077874bfb8229080265073d7e1e14a9
|
|
| MD5 |
36472626e3b2556a50742b05251ff88b
|
|
| BLAKE2b-256 |
58b660c1fd140a0ffc8a7aa16bc0a3f24a85b1dedca8893c7efc7f412d436ebe
|