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 PayChains

pc = 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

PlanRate Limit

|------|-----------|

Free100 req/min
Pro1,000 req/min
Enterprise5,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

FieldTypeRequiredDescription

|-------|------|----------|-------------|

amount_usdnumberYesAmount in USD
chainstringYesethereum, polygon, bsc, arbitrum, base
tokenstringYesUSDC, USDT, ETH, MATIC, BNB
metadataobjectNoCustom key-value metadata
payment_link_idstringNoLink 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
Try It Live

Subscriptions

Create Subscription

POST /api/v1/subscriptions

FieldTypeRequiredDescription

|-------|------|----------|-------------|

plan_namestringYesPlan display name
amount_usdnumberYesRecurring amount in USD
intervalstringYesweekly, monthly, quarterly, yearly
customer_emailstringNoCustomer email for notifications
customer_walletstringNoCustomer wallet address
preferred_chainstringNoDefault chain for billing
preferred_tokenstringNoDefault 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).

Try It Live

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 funds
  • payment.confirming — Transaction detected on-chain
  • payment.completed — Payment fully confirmed
  • payment.failed — Payment failed
  • payment.expired — Payment expired (30 min window)
  • subscription.created — New subscription created
  • subscription.payment_due — Recurring payment initiated
  • subscription.cancelled — Subscription cancelled
Try It Live

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.

Try It Live

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.

Try It Live

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

CodeMeaning

|------|---------|

200Success
201Created
400Bad request (validation error)
401Unauthorized (invalid/missing API key)
404Resource not found
429Rate limit exceeded
500Internal 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, ValidationError

try:

payment = pc.payments.create(...)

except RateLimitError:

# Back off and retry

pass

except ValidationError as e:

print(e.message)