API Documentation
Everything you need to integrate PayChains into your application.
Getting Started
1. Create an account
Register at paychains.dev/auth/register to get your API keys. You'll receive a live key (pc_live_...) and a test key (pc_test_...).
2. Configure your wallet
Go to Dashboard → Settings and paste your EVM wallet address (0x...).
All payments will be sent directly to this address — your private keys never leave your wallet.
Supported wallets: MetaMask, Coinbase Wallet, Ledger, Trezor, or any EVM wallet.
3. Install the SDK
npm install paychains
# or
pip install paychains
Or use the REST API directly — all endpoints accept JSON and return JSON.
4. Make your first API call
import PayChains from 'paychains';const pc = new PayChains({ apiKey: 'pc_test_YOUR_KEY' });
const payment = await pc.payments.create({
amount_usd: 25.00,
chain: 'polygon',
token: 'USDC'
});
console.log(payment.deposit_address); // 0x7a3b...
console.log(payment.id); // uuid
Python
from paychains import PayChainspc = PayChains(api_key="pc_test_YOUR_KEY")
payment = pc.payments.create(
amount_usd=25.00,
chain="polygon",
token="USDC"
)
print(payment["deposit_address"])
Authentication
All API requests require an API key passed via the X-API-Key header.
X-API-Key: pc_live_your_api_key_here
Test vs Live keys: Use pc_test_... keys for development. Test payments are processed on testnets. Use pc_live_... keys for production.
Rate Limits
|------|-----------|
| Free | 100 req/min |
|---|---|
| Pro | 1,000 req/min |
| Enterprise | 5,000 req/min |
Rate limit headers are included in every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1679012345
Payments API
Create Payment
POST /api/v1/payments/create
|-------|------|----------|-------------|
| amount_usd | number | Yes | Amount in USD |
|---|---|---|---|
| chain | string | Yes | ethereum, polygon, bsc, arbitrum, base |
| token | string | Yes | USDC, USDT, ETH, MATIC, BNB |
| metadata | object | No | Custom key-value metadata |
| payment_link_id | string | No | Link to a payment link |
Note: Solana and Bitcoin are coming soon.
List Payments
GET /api/v1/payments
Query params: status, chain, page, per_page
Get Payment
GET /api/v1/payments/{id}
Refund Payment
POST /api/v1/payments/{id}/refund
Only completed payments can be refunded.
Payment Statuses
- pending — Waiting for customer to send funds
- confirming — Transaction detected on-chain, awaiting confirmations
- completed — Payment confirmed and settled
- failed — Payment failed
- expired — Payment window expired (30 minutes)
- refunded — Payment has been refunded
Payment Links
Create Payment Link
POST /api/v1/payment-links
|-------|------|----------|-------------|
| title | string | Yes | Display name shown on checkout |
|---|---|---|---|
| description | string | No | Description shown to customer |
| amount_usd | number | No | Fixed amount (omit for custom amount) |
List Payment Links
GET /api/v1/payment-links
Payment links generate a hosted checkout page at /checkout/{link_id} that customers can use to select a chain, token, and pay via QR code.
Subscriptions
Create Subscription
POST /api/v1/subscriptions
|-------|------|----------|-------------|
| plan_name | string | Yes | Plan display name |
|---|---|---|---|
| amount_usd | number | Yes | Recurring amount in USD |
| interval | string | Yes | weekly, monthly, quarterly, yearly |
| customer_email | string | No | Customer email for notifications |
| customer_wallet | string | No | Customer wallet address |
| preferred_chain | string | No | Default chain for billing |
| preferred_token | string | No | Default token for billing |
List Subscriptions
GET /api/v1/subscriptions
Subscriptions are automatically billed on schedule. Failed payments trigger retry logic with exponential backoff (up to 3 retries).
Webhooks
Configure your webhook URL in Settings. PayChains sends POST requests with HMAC-SHA256 signatures for all payment events.
Webhook Payload
{
"event": "payment.completed",
"data": {
"payment_id": "uuid",
"status": "completed",
"amount_usd": "25.00",
"token": "USDC",
"chain": "ethereum",
"tx_hash": "0x...",
"deposit_address": "0x...",
"confirmations": 5
},
"timestamp": "2026-03-17T00:00:00Z"
}
Verifying Signatures
import { createHmac } from 'crypto';function verify(body, signature, secret) {
const expected = createHmac('sha256', secret)
.update(body)
.digest('hex');
return expected === signature;
}
Or use the SDK:
const isValid = paychains.webhooks.verifySignature(
requestBody, headers['x-signature'], webhookSecret
);
Events
payment.pending— Payment created, waiting for fundspayment.confirming— Transaction detected on-chainpayment.completed— Payment fully confirmedpayment.failed— Payment failedpayment.expired— Payment expired (30 min window)subscription.created— New subscription createdsubscription.payment_due— Recurring payment initiatedsubscription.cancelled— Subscription cancelled
Payouts
PayChains is non-custodial — payments go directly to your wallet address. No payout requests needed.
How it works
When a customer pays, funds are sent directly to your configured wallet address. You have immediate access to all funds in your wallet.
List Historical Payouts
GET /api/v1/payouts
Returns historical payout records (legacy). New payments are deposited directly to your wallet.
Analytics
Overview
GET /api/v1/analytics/overview
Query params: days (default: 30)
Returns total volume, payment count, and success rate for the specified period.
By Chain
GET /api/v1/analytics/by-chain
Returns payment volume breakdown by blockchain.
By Token
GET /api/v1/analytics/by-token
Returns payment volume breakdown by token.
WebSocket Updates
Get real-time payment status updates via WebSocket:
const ws = new WebSocket('wss://api.paychains.dev/ws/payments/{payment_id}');ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.status); // "confirming"
console.log(data.confirmations); // 3
console.log(data.required_confirmations); // 5
};
ws.onclose = () => {
// Payment reached terminal state
};
The WebSocket sends a JSON message whenever the payment status or confirmation count changes. It automatically closes when the payment reaches a terminal state (completed, failed, expired, refunded).
WebSocket Message Format
{
"payment_id": "uuid",
"status": "confirming",
"confirmations": 3,
"required_confirmations": 5,
"amount_usd": "25.00",
"amount_crypto": "25.123456",
"token": "USDC",
"chain": "polygon",
"tx_hash": "0x...",
"deposit_address": "0x..."
}
Error Handling
All errors return a JSON response with a detail field:
{
"detail": "Invalid API key"
}
HTTP Status Codes
|------|---------|
| 200 | Success |
|---|---|
| 201 | Created |
| 400 | Bad request (validation error) |
| 401 | Unauthorized (invalid/missing API key) |
| 404 | Resource not found |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
SDK Error Handling
try {
const payment = await pc.payments.create({ ... });
} catch (error) {
if (error.status === 429) {
// Rate limited — back off and retry
}
console.error(error.message);
}
from paychains.exceptions import RateLimitError, ValidationErrortry:
payment = pc.payments.create(...)
except RateLimitError:
# Back off and retry
pass
except ValidationError as e:
print(e.message)