📦 Node.js SDK & CLI
Use our official SDK for seamless integration with your Node.js applications
🔐 Non-Custodial Web Wallet
Multi-chain wallet for humans and AI agents — no signup, no KYC, API-first
🔐 Escrow Service
Trustless crypto escrow — hold funds until both sides are satisfied. Token-based auth, no accounts needed.
💳 Credit Card Payments
Accept credit & debit cards via Stripe Connect — gateway mode, escrow mode, and automatic merchant onboarding
⚡ x402 Payment Protocol
HTTP-native machine payments — the only multi-chain x402 facilitator. BTC, ETH, SOL, USDC, Lightning & more.
🛡️ Reputation & DID
Decentralized reputation system — track agent performance, issue verifiable credentials, query trust scores
x402 Payment Protocol
HTTP-native machine payments using the HTTP 402 Payment Required status code. Paywall any API route — clients (browsers, AI agents, bots) automatically negotiate payment inline with HTTP requests. CoinPayPortal is the only multi-chain x402 facilitator: BTC, ETH, SOL, POL, BCH, USDC (4 chains), Lightning, and Stripe.
How x402 Works
- Client requests a paid API endpoint (e.g.
GET /api/premium) - Server returns 402 with an
accepts[]array listing all payment methods + prices - Client picks a method (BTC, USDC, Lightning, card...) and creates a payment proof
- Client retries the request with an
X-Paymentheader containing the proof - Server verifies the proof via CoinPayPortal's facilitator and serves the content
Merchant: Paywall a Route
Install the SDK and add x402 middleware to any Express or Next.js route. Set a USD price — the middleware handles multi-chain pricing and 402 responses automatically.
npm install @profullstack/coinpay
Express
import { createX402Middleware } from '@profullstack/coinpay';
const x402 = createX402Middleware({
apiKey: 'cp_live_xxxxx', // from /businesses
payTo: {
bitcoin: 'bc1qYourBtcAddress',
ethereum: '0xYourEvmAddress', // also receives USDC on ETH
polygon: '0xYourEvmAddress',
base: '0xYourEvmAddress', // also receives USDC on Base
solana: 'YourSolanaAddress',
lightning: 'lno1YourBolt12Offer',
stripe: 'acct_YourStripeId',
'bitcoin-cash': 'bitcoincash:qYourBchAddress',
},
rates: { BTC: 65000, ETH: 3500, SOL: 150, POL: 0.50, BCH: 350 },
});
// Paywall — charge $5, buyer picks their chain/asset
app.get('/api/premium', x402({ amountUsd: 5.00 }), (req, res) => {
res.json({ data: 'premium content', paidWith: req.x402Payment });
});Next.js (App Router)
import { buildPaymentRequired, verifyX402Payment } from '@profullstack/coinpay';
export async function GET(request: Request) {
const paymentHeader = request.headers.get('x-payment');
if (!paymentHeader) {
// No payment — return 402 with all accepted methods
const body = buildPaymentRequired({
payTo: {
bitcoin: 'bc1q...',
ethereum: '0x...',
solana: 'So1...',
lightning: 'lno1...',
},
amountUsd: 5.00,
rates: { BTC: 65000, ETH: 3500, SOL: 150 },
});
return Response.json(body, { status: 402 });
}
// Verify payment proof via CoinPayPortal facilitator
const result = await verifyX402Payment(paymentHeader, {
apiKey: 'cp_live_xxxxx',
});
if (!result.valid) {
return Response.json({ error: result.reason }, { status: 402 });
}
return Response.json({ data: 'premium content' });
}Customer: How to Pay
When you hit an x402-protected endpoint, you receive a 402 response with the payment options. Here's what the response looks like and how to pay with each method.
The 402 Response
{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "bitcoin",
"asset": "BTC",
"maxAmountRequired": "769",
"payTo": "bc1qMerchant...",
"extra": { "label": "Bitcoin" }
},
{
"scheme": "exact",
"network": "base",
"asset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"maxAmountRequired": "5000000",
"payTo": "0xMerchant...",
"extra": { "label": "USDC on Base", "chainId": 8453 }
},
{
"scheme": "exact",
"network": "lightning",
"asset": "BTC",
"maxAmountRequired": "769",
"payTo": "lno1Merchant...",
"extra": { "label": "Lightning" }
},
{
"scheme": "exact",
"network": "stripe",
"asset": "USD",
"maxAmountRequired": "500",
"payTo": "acct_MerchantStripe",
"extra": { "label": "Card (Stripe)" }
}
],
"error": "Payment required"
}Payment Methods
| Method | How to Pay | Proof |
|---|---|---|
| USDC (EVM) | Sign an EIP-712 typed message authorizing transferFrom — gasless, no on-chain tx until settlement | signature + authorization object |
| Bitcoin / BCH | Broadcast a transaction to the merchant's payTo address | Transaction ID (txid) |
| Lightning | Pay the BOLT12 offer via any Lightning wallet | Payment preimage |
| Solana | Sign and broadcast a SOL/USDC transfer | Transaction signature |
| Stripe (Card) | Complete card checkout (redirect or embedded form) | Payment Intent ID |
Sending the Payment Proof
After paying, retry the original request with the proof in the X-Payment header (base64-encoded JSON):
GET /api/premium HTTP/1.1 Host: api.example.com X-Payment: eyJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJiYXNlIi4uLn0=
Decoded payload (USDC on Base example):
{
"scheme": "exact",
"network": "base",
"asset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"payload": {
"signature": "0xabc123...",
"authorization": {
"from": "0xBuyerAddress...",
"to": "0xMerchantAddress...",
"value": "5000000",
"validAfter": 0,
"validBefore": 1739980800,
"nonce": "0xUniqueNonce..."
}
}
}Paying with CoinPay Web Wallet
If the customer has a CoinPay Web Wallet, the flow is seamless — the wallet can read the 402 response, display the payment options, and sign the proof automatically:
// Using CoinPay Wallet SDK (browser)
import { CoinPayWallet } from '@profullstack/coinpay/wallet';
const wallet = new CoinPayWallet();
// Fetch with automatic x402 handling
const response = await wallet.x402fetch('https://api.example.com/premium');
// Wallet prompts user to pick a chain → signs → retries → done
const data = await response.json();Paying with Any Wallet (Programmatic)
AI agents, bots, or any programmatic client can use x402fetch() — it wraps fetch() and handles the entire 402 → pay → retry loop:
import { x402fetch } from '@profullstack/coinpay';
const response = await x402fetch('https://api.example.com/premium', {
paymentMethods: {
// Provide wallet/signer for each chain you can pay with
base: { signer: evmWallet }, // ethers.js or viem signer
lightning: { macaroon, host }, // LND or CLN credentials
bitcoin: { wif: 'L4rK1yD...' }, // BTC private key (WIF)
solana: { secretKey: keypair }, // Solana Keypair
},
preferredMethod: 'usdc_base', // try this first
maxAmount: '10.00', // USD safety cap
});
const data = await response.json();
// data = { data: 'premium content' }Manual Flow (cURL)
# Step 1: Hit the endpoint — get 402 with payment options
curl -s https://api.example.com/api/premium | jq .
# → { "x402Version": 1, "accepts": [...], "error": "Payment required" }
# Step 2: Pay using your preferred method (e.g. send BTC to payTo address)
# ... broadcast transaction, get txid ...
# Step 3: Retry with the payment proof
curl -H "X-Payment: $(echo -n '{"scheme":"exact","network":"bitcoin","asset":"BTC","payload":{"txid":"abc123..."}}' | base64)" \
https://api.example.com/api/premium
# → { "data": "premium content" }Fees
CoinPayPortal takes a small commission on each x402 payment, deducted before forwarding to the merchant:
| Plan | Commission | Merchant Receives |
|---|---|---|
| Starter (Free) | 1.0% | 99.0% |
| Professional ($49/mo) | 0.5% | 99.5% |
Network fees (gas, miner fees) are separate and vary by chain. Lightning has near-zero network fees. No hidden fees.
Facilitator API
The facilitator endpoints are used by the merchant's middleware to verify and settle payments. You typically don't call these directly — the SDK handles it.
/api/x402/verifyVerify an x402 payment proof. Validates signatures, checks expiry, prevents replay attacks.
// Request
{
"proof": "<base64-encoded X-Payment header>",
"expectedAmount": "5000000",
"expectedAsset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"expectedNetwork": "base",
"expectedPayTo": "0xMerchantAddress..."
}
// Response (200 OK)
{
"valid": true,
"network": "base",
"asset": "USDC",
"amount": "5000000",
"from": "0xBuyerAddress...",
"to": "0xMerchantAddress..."
}
// Response (invalid)
{
"valid": false,
"reason": "Signature expired"
}/api/x402/settleSettle (claim) a verified payment on-chain. For USDC, executes the transferFrom. Called after successful verification.
// Request
{
"proof": "<base64-encoded X-Payment header>",
"network": "base"
}
// Response (200 OK)
{
"settled": true,
"txHash": "0xdef456...",
"network": "base",
"amount": "5000000",
"asset": "USDC"
}💡 Tip: For a full interactive setup guide and payment history dashboard, visit the x402 Dashboard.
Web Wallet API
Non-custodial multi-chain wallet for humans and AI agents. Private keys never leave the client. The server stores only public keys and coordinates transactions. No email, no KYC — your seed phrase is your identity.
Keys are generated and stored client-side only. The server never sees your seed phrase or private keys. Authentication uses cryptographic signature challenges — prove you own the key without revealing it.
Supported Chains
BTCBitcoin
BCHBitcoin Cash
ETHEthereum
POLPolygon
SOLSolana
USDC_ETHUSDC (Ethereum)
USDC_POLUSDC (Polygon)
USDC_SOLUSDC (Solana)
Installation
SDK (npm)
Install
# Payment gateway SDK + CLI npm install -g @profullstack/coinpay # Configure coinpay config set apiKey YOUR_API_KEY coinpay config set apiUrl https://coinpayportal.com
Wallet CLI (from source)
Install
git clone https://github.com/profullstack/coinpayportal cd coinpayportal && pnpm install # Create a wallet pnpm coinpay-wallet create --chains BTC,ETH,SOL
Wallet CLI Commands
coinpay-wallet create--words 12|24 --chains BTC,ETH,...Create a new wallet with mnemonic
coinpay-wallet import<mnemonic> --chains BTC,ETH,...Import wallet from seed phrase
coinpay-wallet balance<wallet-id>Show balances with USD values
coinpay-wallet address<wallet-id> --chain ETHList or filter addresses
coinpay-wallet send<wallet-id> --from <addr> --to <addr> --chain ETH --amount 0.5Send a transaction
coinpay-wallet history<wallet-id> --chain BTC --limit 20View transaction history
coinpay-wallet sync<wallet-id> --chain SOLSync on-chain deposits into history
coinpay-wallet derive-missing<wallet-id> [--chains BTC,ETH,...]Derive addresses for newly supported chains
Environment Variables
COINPAY_API_URL — API base URL (default: http://localhost:8080)
COINPAY_AUTH_TOKEN — JWT token for read-only operations
COINPAY_MNEMONIC — Mnemonic phrase (required for send and derive-missing)
Or use a config file: ~/.coinpayrc.json
Authentication
Option 1: Signature Auth (per-request)
Sign each request with your secp256k1 key. No tokens to manage or refresh.
Authorization Header
Authorization: Wallet <wallet_id>:<signature>:<timestamp>:<nonce>
Message to sign: {METHOD}:{PATH}:{UNIX_TIMESTAMP}:{BODY}
Example: GET:/api/web-wallet/abc123/balances:1706432100:
Nonce: random string (e.g. crypto.randomUUID().slice(0,8))
Prevents replay when firing concurrent requests in the same second.
Optional for backwards compatibility but recommended.Option 2: Challenge → JWT (session-based)
- Client requests a challenge:
GET /api/web-wallet/auth/challenge?wallet_id=UUID - Server returns a random challenge string with expiration
- Client signs the challenge with their private key
- Client submits signature:
POST /api/web-wallet/auth/verify - Server verifies and returns a JWT (valid 24h)
Quick Start for AI Agents
The fastest way to integrate is with the SDK. One call creates a wallet with addresses for all 8 chains — ready to send and receive immediately.
Node.js SDK — Create Wallet (All Chains)
import { Wallet } from '@coinpayportal/wallet-sdk';
// Creates wallet + derives addresses for BTC, BCH, ETH, POL, SOL, USDC×3
const wallet = await Wallet.create({
baseUrl: 'https://coinpayportal.com',
chains: ['BTC', 'BCH', 'ETH', 'POL', 'SOL', 'USDC_ETH', 'USDC_POL', 'USDC_SOL'],
});
// All addresses are ready immediately
const addresses = await wallet.getAddresses();
console.log(addresses);
// [{ chain: 'BTC', address: 'bc1q...' }, { chain: 'ETH', address: '0x...' }, ...]
// Check balances
const balances = await wallet.getBalances();
// Send a transaction
const tx = await wallet.send({
chain: 'SOL', to: 'recipient...', amount: '0.1'
});
// Derive additional addresses if needed (e.g. fresh BTC receive address)
const newAddr = await wallet.deriveAddress('BTC');Upgrading Existing Wallets
When CoinPayPortal adds support for new coins, existing wallets won't have addresses for them. Use deriveMissingChains() to automatically derive addresses for any missing chains.
Node.js SDK — Derive Missing Chains
import { Wallet } from '@coinpayportal/wallet-sdk';
// Load your existing wallet from seed
const wallet = await Wallet.fromSeed(process.env.WALLET_MNEMONIC, {
baseUrl: 'https://coinpayportal.com',
chains: ['BTC', 'ETH'], // minimal chains for auth
});
// Check which chains are missing
const missing = await wallet.getMissingChains();
console.log('Missing chains:', missing);
// ['BCH', 'POL', 'SOL', 'USDC_ETH', 'USDC_POL', 'USDC_SOL']
// Derive all missing chains at once
const newAddresses = await wallet.deriveMissingChains();
console.log('New addresses:', newAddresses);
// [{ chain: 'BCH', address: 'bitcoincash:q...' }, ...]CLI Command (for bots)
Terminal
# Check and derive missing chains export COINPAY_MNEMONIC="your twelve word seed phrase here" pnpm coinpay-wallet derive-missing <wallet-id> # Output: # Missing chains: BCH, POL, SOL, USDC_ETH, USDC_POL, USDC_SOL # Deriving addresses... # New addresses derived: # Chain Address Index # BCH bitcoincash:q... 0 # POL 0x... 0 # ...
/api/web-wallet/createRegister a new wallet. Client generates seed phrase and HD keys locally, then sends only public keys. Include initial_addresses for all chains to have addresses ready immediately — no separate derive calls needed.
💡 Auto-derive all chains: Include initial_addresses for all 8 supported chains in the create request. The SDK does this automatically. Addresses are ready to use immediately — no separate derive calls needed.
Request Body
{
"public_key_secp256k1": "04a1b2c3d4...",
"public_key_ed25519": "abc123...",
"initial_addresses": [
{ "chain": "BTC", "address": "bc1q...", "derivation_path": "m/44'/0'/0'/0/0" },
{ "chain": "BCH", "address": "bitcoincash:q...", "derivation_path": "m/44'/145'/0'/0/0" },
{ "chain": "ETH", "address": "0x...", "derivation_path": "m/44'/60'/0'/0/0" },
{ "chain": "POL", "address": "0x...", "derivation_path": "m/44'/60'/0'/0/0" },
{ "chain": "SOL", "address": "ABC...", "derivation_path": "m/44'/501'/0'/0'" },
{ "chain": "USDC_ETH", "address": "0x...", "derivation_path": "m/44'/60'/0'/0/0" },
{ "chain": "USDC_POL", "address": "0x...", "derivation_path": "m/44'/60'/0'/0/0" },
{ "chain": "USDC_SOL", "address": "ABC...", "derivation_path": "m/44'/501'/0'/0'" }
]
}Response (201)
{
"ok": true,
"data": {
"wallet_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2025-01-15T10:30:00.000Z",
"addresses": [
{ "chain": "BTC", "address": "bc1q...", "derivation_index": 0 },
{ "chain": "BCH", "address": "bitcoincash:q...", "derivation_index": 0 },
{ "chain": "ETH", "address": "0x...", "derivation_index": 0 },
{ "chain": "POL", "address": "0x...", "derivation_index": 0 },
{ "chain": "SOL", "address": "ABC...", "derivation_index": 0 },
{ "chain": "USDC_ETH", "address": "0x...", "derivation_index": 0 },
{ "chain": "USDC_POL", "address": "0x...", "derivation_index": 0 },
{ "chain": "USDC_SOL", "address": "ABC...", "derivation_index": 0 }
]
}
}/api/web-wallet/importImport an existing wallet with proof of ownership (signature over a timestamped message).
Request Body
{
"identity_public_key": "04a1b2c3d4...",
"proof_signature": "3045022100...",
"proof_message": "CoinPay wallet import: 2025-01-15T10:30:00Z",
"label": "Imported Wallet",
"addresses": [
{ "chain": "ETH", "address": "0x...", "public_key": "04...", "derivation_path": "m/44'/60'/0'/0/0" }
]
}/api/web-wallet/auth/challengeRequest an authentication challenge. Pass wallet_id as query parameter.
Response
{
"ok": true,
"data": {
"challenge_id": "ch_123",
"challenge": "coinpay:auth:550e8400...:1705312500:a1b2c3d4e5f6",
"expires_at": "2025-01-15T10:35:00.000Z"
}
}/api/web-wallet/auth/verifyVerify a signed challenge and receive a JWT token (valid 24h).
Request Body
{
"wallet_id": "550e8400-e29b-41d4-a716-446655440000",
"challenge_id": "ch_123",
"signature": "3045022100..."
}Response
{
"ok": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2025-01-16T10:30:00.000Z",
"wallet_id": "550e8400..."
}
}/api/web-wallet/:idGet wallet info. Requires wallet JWT.
/api/web-wallet/:id/deriveDerive an additional address for a chain. Not needed after wallet creation — initial addresses are auto-generated. Use this to create fresh receive addresses (e.g. for privacy on BTC).
Request Body
{
"chain": "ETH",
"address": "0xnewaddress...",
"public_key": "04...",
"derivation_path": "m/44'/60'/0'/0/1"
}/api/web-wallet/:id/addressesList all addresses. Optional filters: chain, active_only.
/api/web-wallet/:id/addresses/:address_idDeactivate an address (soft delete).
/api/web-wallet/:id/balancesGet balances for all active addresses. Optional: chain filter, refresh=true to force blockchain query.
Response
{
"ok": true,
"data": {
"balances": [
{ "chain": "BTC", "address": "bc1q...", "balance": "0.05423", "last_updated": "2025-01-15T10:30:00Z" },
{ "chain": "ETH", "address": "0x...", "balance": "1.234", "last_updated": "2025-01-15T10:30:00Z" }
]
}
}/api/web-wallet/:id/balances/total-usdGet total wallet balance converted to USD with per-chain breakdown.
/api/web-wallet/:id/prepare-txPrepare an unsigned transaction for client-side signing. Expires in 5 minutes.
Request Body
{
"from_address": "0xSenderAddress...",
"to_address": "0xRecipientAddress...",
"chain": "ETH",
"amount": "0.5",
"priority": "medium"
}Response
{
"ok": true,
"data": {
"tx_id": "tx_789",
"unsigned_tx": "0x02f8...",
"chain": "ETH",
"estimated_fee": "0.002",
"expires_at": "2025-01-15T10:45:00.000Z"
}
}Security: If spend limits or address whitelists are enabled, the server enforces them at this step. Errors: SPEND_LIMIT_EXCEEDED, ADDRESS_NOT_WHITELISTED, INSUFFICIENT_BALANCE
/api/web-wallet/:id/broadcastBroadcast a signed transaction to the network.
Request Body
{
"tx_id": "tx_789",
"signed_tx": "0x02f8...",
"chain": "ETH"
}Response
{
"ok": true,
"data": {
"tx_hash": "0xabc123...",
"chain": "ETH",
"status": "pending",
"explorer_url": "https://etherscan.io/tx/0xabc123..."
}
}/api/web-wallet/:id/estimate-feeGet fee estimates (low/medium/high) for a chain.
Response
{
"ok": true,
"data": {
"chain": "ETH",
"estimates": {
"low": { "fee": "0.0005", "time_minutes": 15 },
"medium": { "fee": "0.001", "time_minutes": 5 },
"high": { "fee": "0.003", "time_minutes": 1 }
}
}
}/api/web-wallet/:id/transactionsGet transaction history with filtering and pagination.
Query Parameters
chain - Filter by chain
direction - incoming or outgoing
status - pending, confirming, confirmed, failed
from_date / to_date - ISO date range
limit - Default 50, max 100
offset - Pagination offset
/api/web-wallet/:id/transactions/:tx_idGet details of a specific transaction.
/api/web-wallet/:id/sync-historySync on-chain transaction history from blockchain explorers. Detects external deposits not initiated through the app. A background daemon also runs server-side every 15 seconds to finalize pending/confirming transactions.
Request Body (chain is optional)
{
"chain": "BTC"
}Background daemon: The server automatically checks pending/confirming transactions every 15 seconds and updates their status. Transactions are finalized even if the client disconnects. Daemon-finalized txs are tagged with metadata.finalized_by: "daemon".
/api/web-wallet/:id/settingsGet wallet security settings (spend limits, whitelist, confirmation delay).
/api/web-wallet/:id/settingsUpdate wallet security settings.
Request Body (all fields optional)
{
"daily_spend_limit": 500.00,
"whitelist_enabled": true,
"whitelist_addresses": ["0xTrustedAddr1...", "0xTrustedAddr2..."],
"require_confirmation": true,
"confirmation_delay_seconds": 60
}/api/web-wallet/:id/webhooksRegister a webhook for wallet events.
Request Body
{
"url": "https://myapp.com/webhooks/wallet",
"events": ["transaction.incoming", "transaction.confirmed"]
}Available Events
transaction.incomingNew incoming transaction detected
transaction.confirmedTransaction reached confirmation threshold
transaction.outgoingOutgoing transaction broadcast
balance.changedBalance updated
/api/web-wallet/:id/webhooksList registered webhooks.
/api/web-wallet/:id/webhooks/:webhook_idRemove a webhook registration.
Rate Limits
5/hour per IP10/min per IP60/min per IP20/min per IP10/min per IP60/min per IP10/min per IP30/min per IPTry the Web Wallet
Create a wallet in seconds — no signup required.
🤖 For AI Agents
Install the SDK, create a wallet, and start sending/receiving crypto in 3 lines of code. All chain addresses are auto-generated — no manual setup needed.
npm install
npm install @coinpayportal/wallet-sdk
Escrow Service
Anonymous, trustless escrow for crypto transactions. Hold funds until both parties are satisfied. No accounts required — authentication is handled via unique tokens returned at creation time.
How Escrow Works
- Create — Specify chain, amount, depositor & beneficiary addresses. Get a deposit address + two auth tokens. Share the Escrow ID!
- Fund — Depositor sends crypto to the escrow address. Auto-detected by the balance monitor.
- Manage — Both parties can manage the escrow via
/escrow/manage?id=xxx&token=yyywith shareable links generated at creation. - Release or Dispute — Depositor releases funds (using
release_token) or either party opens a dispute. - Settlement — Funds forwarded on-chain to beneficiary (minus fee). Refunds return the full amount.
Auth Tokens
release_token — Given to depositor. Used to release or refund funds.
beneficiary_token — Given to beneficiary. Used to open disputes.
Fees
Free tier: 1% on release
Professional: 0.5% on release
Refunds: No fee (full amount returned)
Escrow Statuses
pendingAwaiting deposit
fundedDeposit received on-chain
releasedDepositor released funds
settledFunds forwarded to beneficiary
disputedDispute opened by either party
refundedFunds returned to depositor
expiredDeposit window expired
/api/escrowCreate a new escrow. No authentication required (anonymous). Optionally authenticate to associate with a business and get paid-tier fees.
Fiat Support: While this API accepts crypto amounts directly, you can now specify amounts in fiat when using the SDK/CLI, which will auto-convert via the rates API before creating the escrow.
Request Body
{
"chain": "ETH", // BTC, BCH, ETH, POL, SOL, USDC, USDC_ETH, USDC_POL, USDC_SOL
"amount": 0.5, // Amount in crypto
"depositor_address": "0xAlice...", // Depositor's wallet
"beneficiary_address": "0xBob...", // Beneficiary's wallet
"arbiter_address": "0xArbiter...", // Optional: dispute arbiter
"expires_in_hours": 48, // Optional: 1-720 hours (default: 24)
"metadata": { "order_id": "123" }, // Optional: custom metadata
"business_id": "uuid" // Optional: for merchant association
}cURL Example
curl -X POST https://coinpayportal.com/api/escrow \
-H "Content-Type: application/json" \
-d '{
"chain": "ETH",
"amount": 0.5,
"depositor_address": "0xAlice...",
"beneficiary_address": "0xBob...",
"expires_in_hours": 48
}'Response (201 Created)
{
"id": "a1b2c3d4-...",
"escrow_address": "0xEscrowAddr...",
"chain": "ETH",
"amount": 0.5,
"amount_usd": 1250.00,
"fee_amount": 0.005,
"status": "created",
"depositor_address": "0xAlice...",
"beneficiary_address": "0xBob...",
"expires_at": "2024-01-03T12:00:00Z",
"created_at": "2024-01-01T12:00:00Z",
"release_token": "esc_abc123...",
"beneficiary_token": "esc_def456...",
"metadata": {}
}Important: Save the release_token and beneficiary_token — they are only returned once at creation time. The depositor needs release_token to release or refund. The beneficiary needs beneficiary_token to dispute. Both parties can use these tokens to manage the escrow via the /escrow/manage page.
Escrow ID: Always save the escrow ID returned in the response. Share the escrow ID and appropriate token with the other party so they can manage the escrow at /escrow/manage?id=xxx&token=yyy
/api/escrow?status=funded&depositor=0x...List escrows. Requires at least one filter: status, depositor, beneficiary, or business_id.
Query Parameters
status — Filter by status (created, funded, released, settled, etc.) depositor — Filter by depositor address beneficiary — Filter by beneficiary address business_id — Filter by business (requires auth) limit — Results per page (default: 20) offset — Pagination offset
Response
{
"escrows": [
{
"id": "a1b2c3d4-...",
"escrow_address": "0xEscrowAddr...",
"chain": "ETH",
"amount": 0.5,
"status": "funded",
"deposited_amount": 0.5,
"funded_at": "2024-01-01T13:00:00Z",
...
}
],
"total": 1,
"limit": 20,
"offset": 0
}/api/escrow/:idGet escrow details by ID. Public endpoint — no auth required.
cURL Example
curl https://coinpayportal.com/api/escrow/a1b2c3d4-...
/api/escrow/:id/authAuthenticate with escrow using token. Returns escrow details and your role (depositor/beneficiary). Used by the manage page to determine what actions are available.
Request Body
{
"token": "esc_abc123..." // release_token or beneficiary_token
}cURL Example
curl -X POST https://coinpayportal.com/api/escrow/a1b2c3d4-.../auth \
-H "Content-Type: application/json" \
-d '{"token": "esc_abc123..."}'Response
{
"escrow": {
"id": "a1b2c3d4-...",
"escrow_address": "0xEscrowAddr...",
"chain": "ETH",
"amount": 0.5,
"status": "funded",
"depositor_address": "0xAlice...",
"beneficiary_address": "0xBob...",
"funded_at": "2024-01-01T13:00:00Z",
...
},
"role": "depositor" // "depositor" or "beneficiary"
}Use Case: Both depositors and recipients can manage escrows via /escrow/manage?id=xxx&token=yyy. This endpoint authenticates the token and returns available actions based on your role and escrow status.
/api/escrow/:id/releaseRelease funds to the beneficiary. Only the depositor (via release_token) can do this. Escrow must be in 'funded' or 'disputed' status.
Request Body
{
"release_token": "esc_abc123..."
}Response
{
"success": true,
"escrow": {
"id": "a1b2c3d4-...",
"status": "released",
"released_at": "2024-01-02T12:00:00Z",
...
}
}After release, the cron monitor triggers on-chain settlement — funds are forwarded to the beneficiary minus the platform fee.
/api/escrow/:id/refundRequest a refund. Only the depositor (via release_token) can do this. Escrow must be in 'funded' status. Full amount is returned (no fee).
Request Body
{
"release_token": "esc_abc123..."
}/api/escrow/:id/disputeOpen a dispute. Either party can do this (depositor via release_token, beneficiary via beneficiary_token). Escrow must be in 'funded' status.
Request Body
{
"token": "esc_abc123...",
"reason": "Work was not delivered as agreed upon in the contract"
}Dispute reason must be at least 10 characters. Disputed escrows can still be released by the depositor or resolved by an arbiter.
/api/escrow/:id/eventsGet the audit log for an escrow — all status changes, deposits, releases, disputes, and settlements.
Response
{
"success": true,
"events": [
{
"id": "evt-1",
"escrow_id": "a1b2c3d4-...",
"event_type": "created",
"actor": "0xAlice...",
"details": { "chain": "ETH", "amount": 0.5 },
"created_at": "2024-01-01T12:00:00Z"
},
{
"id": "evt-2",
"event_type": "funded",
"actor": "system",
"details": { "deposited_amount": 0.5, "tx_hash": "0x..." },
"created_at": "2024-01-01T13:00:00Z"
}
]
}SDK & CLI
Node.js SDK
import { CoinPayClient } from '@profullstack/coinpay';
const client = new CoinPayClient({ apiKey: 'YOUR_API_KEY' });
// Create escrow
const escrow = await client.createEscrow({
chain: 'SOL',
amount: 10,
depositor_address: 'Alice...',
beneficiary_address: 'Bob...',
});
console.log('Deposit to:', escrow.escrow_address);
console.log('Release token:', escrow.release_token);
// Check status
const status = await client.getEscrow(escrow.id);
// Release funds
await client.releaseEscrow(escrow.id, escrow.release_token);
// Wait for settlement
const settled = await client.waitForEscrow(escrow.id, 'settled');CLI
# Create escrow coinpay escrow create --chain SOL --amount 10 \ --depositor Alice... --beneficiary Bob... # Check status coinpay escrow get <escrow_id> # List escrows coinpay escrow list --status funded # Release funds coinpay escrow release <escrow_id> --token esc_abc123... # Refund coinpay escrow refund <escrow_id> --token esc_abc123... # Open dispute coinpay escrow dispute <escrow_id> --token esc_def456... \ --reason "Work not delivered as agreed" # View audit log coinpay escrow events <escrow_id>
Recurring Escrow Series
Recurring escrow series automate periodic escrow payments for ongoing work — freelance retainers, milestone-based projects, or any situation where you need regular, trustless payments. Supports both crypto and credit card payments.
How Recurring Escrow Works
- Create Series — Define the amount, interval (weekly/biweekly/monthly), payment method, and beneficiary.
- Auto-Charge — The payment monitor daemon automatically creates and funds a new escrow each period. No cron needed.
- Individual Release — The merchant reviews and releases each escrow individually when work is delivered.
- Manage Anytime — Pause, resume, or cancel the series at any time.
Supported Intervals
weekly — Every 7 days
biweekly — Every 14 days
monthly — Every calendar month
Payment Methods
crypto — On-chain escrow (BTC, ETH, SOL, POL, USDC, etc.)
card — Stripe Connect card payments held in escrow
Integrated Daemon — No Cron Needed
Recurring escrow is powered by the built-in payment monitor daemon. When a series is active, the monitor automatically creates and charges new escrows at each interval. Each child escrow follows the standard escrow lifecycle (created → funded → released → settled).
Series Statuses
activeSeries is running, auto-charges each period
pausedTemporarily stopped, can resume
completedAll periods fulfilled (max_periods reached)
cancelledPermanently stopped by user
/api/escrow/seriesCreate a new recurring escrow series. Requires authentication.
Request Body
{
"business_id": "uuid",
"payment_method": "crypto", // "crypto" or "card"
"customer_email": "client@example.com",
"description": "Weekly retainer — frontend development",
"amount": 500,
"currency": "USD",
"coin": "USDC_SOL", // Required for crypto method
"interval": "weekly", // "weekly", "biweekly", "monthly"
"max_periods": 12, // Optional: auto-complete after N periods
"beneficiary_address": "0xBob...", // Required for crypto method
"stripe_account_id": "acct_..." // Required for card method
}Response (201 Created)
{
"id": "series_abc123",
"business_id": "uuid",
"payment_method": "crypto",
"status": "active",
"interval": "weekly",
"amount": 500,
"currency": "USD",
"coin": "USDC_SOL",
"periods_completed": 0,
"max_periods": 12,
"next_charge_at": "2024-01-08T00:00:00Z",
"created_at": "2024-01-01T00:00:00Z"
}/api/escrow/series?business_id=uuid&status=activeList escrow series for a business. Optionally filter by status.
Response
{
"series": [
{
"id": "series_abc123",
"status": "active",
"interval": "weekly",
"amount": 500,
"currency": "USD",
"periods_completed": 3,
"max_periods": 12,
"next_charge_at": "2024-01-22T00:00:00Z"
}
],
"total": 1
}/api/escrow/series/:idGet series details including all child escrows.
Response
{
"series": { "id": "series_abc123", "status": "active", ... },
"escrows": [
{ "id": "esc_1", "status": "settled", "amount": 500, "period": 1 },
{ "id": "esc_2", "status": "released", "amount": 500, "period": 2 },
{ "id": "esc_3", "status": "funded", "amount": 500, "period": 3 }
]
}/api/escrow/series/:idUpdate a series — pause, resume, or change amount.
Request Body
{
"status": "paused", // "paused" or "active" (to resume)
"amount": 600 // Optional: change amount for future periods
}/api/escrow/series/:idCancel a series permanently. In-flight escrows are not affected.
Response
{
"success": true,
"series": { "id": "series_abc123", "status": "cancelled" }
}Exchange Rates
Get real-time cryptocurrency exchange rates in multiple fiat currencies. Used for price conversion and fiat amount support.
/api/ratesGet exchange rates for cryptocurrencies with multi-fiat support.
Query Parameters
coin - Single cryptocurrency code (e.g., BTC, ETH, SOL)
coins - Comma-separated list of cryptocurrency codes
fiat - Target fiat currency (default: USD)
Supported Fiat Currencies
USDEURGBPCADAUDJPYCHFCNYINRBRLSingle Rate Example
# Get SOL price in EUR curl "https://coinpayportal.com/api/rates?coin=SOL&fiat=EUR"
Multiple Rates Example
# Get multiple cryptocurrency rates in USD (default) curl "https://coinpayportal.com/api/rates?coins=BTC,ETH,SOL" # Get multiple rates in EUR curl "https://coinpayportal.com/api/rates?coins=BTC,ETH,SOL&fiat=EUR"
Single Rate Response
{
"success": true,
"coin": "SOL",
"rate": 185.42,
"fiat": "EUR",
"cached": true,
"timestamp": "2024-01-15T10:30:00Z"
}Multiple Rates Response
{
"success": true,
"rates": {
"BTC": 42350.80,
"ETH": 2580.45,
"SOL": 185.42
},
"fiat": "EUR",
"timestamp": "2024-01-15T10:30:00Z"
}Node.js Example
// Get single rate
const response = await fetch('https://coinpayportal.com/api/rates?coin=SOL&fiat=EUR');
const data = await response.json();
console.log(`1 SOL = €${data.rate}`);
// Get multiple rates
const multiResponse = await fetch('https://coinpayportal.com/api/rates?coins=BTC,ETH,SOL&fiat=GBP');
const multiData = await multiResponse.json();
console.log(`BTC: £${multiData.rates.BTC}`);Integration: This API powers fiat amount support in the SDK and CLI. When you specify --amount-fiat 50 --fiat EUR, it automatically converts to the required crypto amount using these rates.
Authentication
All API requests require authentication using a JWT token in the Authorization header.
/api/auth/registerCreate a new merchant account.
Request Body
{
"email": "merchant@example.com",
"password": "SecurePassword123!",
"name": "My Business" // optional
}cURL Example
curl -X POST https://coinpayportal.com/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "merchant@example.com",
"password": "SecurePassword123!",
"name": "My Business"
}'Node.js Example
const response = await fetch('https://coinpayportal.com/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'merchant@example.com',
password: 'SecurePassword123!',
name: 'My Business'
})
});
const data = await response.json();
console.log(data.token); // Save this token/api/auth/loginLogin to get an authentication token.
Request Body
{
"email": "merchant@example.com",
"password": "SecurePassword123!"
}Response
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"merchant": {
"id": "merchant-123",
"email": "merchant@example.com",
"name": "My Business"
}
}/api/auth/meGet current authenticated merchant information.
cURL Example
curl https://coinpayportal.com/api/auth/me \ -H "Authorization: Bearer YOUR_TOKEN"
Response
{
"success": true,
"merchant": {
"id": "merchant-123",
"email": "merchant@example.com",
"name": "My Business",
"created_at": "2024-01-01T12:00:00Z"
}
}Subscriptions & Entitlements
CoinPay Portal offers two subscription tiers with different features and transaction limits. All subscription payments are processed using cryptocurrency.
Tip: View our pricing page for a visual comparison of plans and to upgrade your subscription.
Subscription Plans
- • Up to 100 transactions/month
- • All supported chains
- • Basic API access
- • Email support
- • Unlimited transactions
- • Priority support
- • Advanced analytics
- • Custom webhooks
/api/subscription-plansGet available subscription plans (public endpoint).
Response
{
"success": true,
"plans": [
{
"id": "starter",
"name": "Starter",
"description": "Perfect for testing and small projects",
"pricing": { "monthly": 0, "yearly": 0 },
"limits": { "monthly_transactions": 100, "is_unlimited": false },
"features": {
"all_chains_supported": true,
"basic_api_access": true,
"advanced_analytics": false,
"custom_webhooks": false,
"white_label": false,
"priority_support": false
}
},
{
"id": "professional",
"name": "Professional",
"pricing": { "monthly": 49, "yearly": 490 },
"limits": { "monthly_transactions": null, "is_unlimited": true },
"features": { ... }
}
]
}/api/entitlementsGet current merchant's entitlements, features, and usage.
cURL Example
curl https://coinpayportal.com/api/entitlements \ -H "Authorization: Bearer YOUR_TOKEN"
Response
{
"success": true,
"entitlements": {
"plan": {
"id": "starter",
"name": "Starter",
"description": "Perfect for testing and small projects",
"price_monthly": 0
},
"features": {
"all_chains_supported": true,
"basic_api_access": true,
"advanced_analytics": false,
"custom_webhooks": false,
"white_label": false,
"priority_support": false
},
"usage": {
"transactions_this_month": 45,
"transaction_limit": 100,
"transactions_remaining": 55,
"is_unlimited": false
},
"status": "active"
}
}/api/subscriptions/checkoutCreate a crypto payment for subscription upgrade.
Request Body
{
"plan_id": "professional",
"billing_period": "monthly", // or "yearly"
"blockchain": "ETH" // BTC, BCH, ETH, POL, SOL
}cURL Example
curl -X POST https://coinpayportal.com/api/subscriptions/checkout \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "professional",
"billing_period": "monthly",
"blockchain": "ETH"
}'Response
{
"success": true,
"payment": {
"id": "pay_abc123",
"payment_address": "0x1234...5678",
"amount": 49,
"currency": "USD",
"blockchain": "ETH",
"expires_at": "2024-01-15T12:00:00Z"
},
"plan": {
"id": "professional",
"name": "Professional",
"billing_period": "monthly",
"price": 49
},
"instructions": "Send exactly $49 worth of ETH to the payment address..."
}/api/subscriptions/statusGet current subscription status.
Response
{
"success": true,
"subscription": {
"planId": "professional",
"status": "active",
"startedAt": "2024-01-01T00:00:00Z",
"endsAt": "2024-02-01T00:00:00Z",
"isActive": true,
"daysRemaining": 15
}
}/api/subscriptions/statusCancel subscription (access continues until end of billing period).
Response
{
"success": true,
"message": "Subscription cancelled. You will retain access until the end of your billing period.",
"subscription": {
"planId": "professional",
"status": "cancelled",
"endsAt": "2024-02-01T00:00:00Z",
"isActive": true,
"daysRemaining": 15
}
}Entitlement Error Codes
Businesses
/api/businessesList all businesses for the authenticated merchant.
cURL Example
curl https://coinpayportal.com/api/businesses \ -H "Authorization: Bearer YOUR_TOKEN"
/api/businessesCreate a new business.
Request Body
{
"name": "My Store",
"description": "Online retail store", // optional
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"webhook_url": "https://mystore.com/webhook" // optional
}Node.js Example
const response = await fetch('https://coinpayportal.com/api/businesses', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'My Store',
wallet_address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
})
});
const data = await response.json();/api/businesses/:idUpdate an existing business.
/api/businesses/:idDelete a business.
Supported Coins
Get the list of supported cryptocurrencies (wallets) configured for a business. This endpoint is useful for displaying available payment options to customers.
/api/supported-coinsGet supported cryptocurrencies for a business.
Query Parameters
business_id - Business UUID (required for JWT auth, not needed with API key)
active_only - If "true", only return active wallets (optional)
cURL Example (API Key)
curl https://coinpayportal.com/api/supported-coins \ -H "Authorization: Bearer cp_live_your_api_key"
cURL Example (JWT with business_id)
curl "https://coinpayportal.com/api/supported-coins?business_id=your-business-uuid" \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Response
{
"success": true,
"coins": [
{
"symbol": "BTC",
"name": "Bitcoin",
"is_active": true,
"has_wallet": true
},
{
"symbol": "ETH",
"name": "Ethereum",
"is_active": true,
"has_wallet": true
},
{
"symbol": "SOL",
"name": "Solana",
"is_active": false,
"has_wallet": true
}
],
"business_id": "your-business-uuid",
"total": 3
}Node.js Example
const response = await fetch('https://coinpayportal.com/api/supported-coins', {
headers: {
'Authorization': 'Bearer cp_live_your_api_key'
}
});
const data = await response.json();
// Display available payment options to customers
data.coins.filter(c => c.is_active).forEach(coin => {
console.log(`Accept ${coin.name} (${coin.symbol})`);
});Available Cryptocurrency Symbols
BTCBitcoin
BCHBitcoin Cash
ETHEthereum
POLPolygon
SOLSolana
USDTTether
USDCUSD Coin
BNBBNB
XRPXRP
ADACardano
DOGEDogecoin
Tip: Use this endpoint to dynamically show customers which cryptocurrencies your business accepts. Only coins with is_active: true should be offered as payment options.
Payments
/api/payments/createCreate a new payment request.
Request Body
{
"business_id": "business-123",
"amount_usd": 100.00,
"currency": "usdc_pol", // See currency options below
"description": "Order #12345", // optional
"redirect_url": "https://yoursite.com/success" // optional - redirect after payment
}Currency Options
Low Fee Options (Recommended)
usdc_pol - USDC on Polygon (~$0.01 fee)
usdc_sol - USDC on Solana (~$0.001 fee)
pol - Polygon native (~$0.01 fee)
sol - Solana native (~$0.001 fee)
Higher Fee Options
btc - Bitcoin (~$2-3 fee)
eth - Ethereum (~$3-5 fee)
usdc_eth - USDC on Ethereum (~$3-5 fee)
usdt - USDT on Ethereum (~$3-5 fee)
Tip: Network fees are added to ensure merchants receive the full amount. Use usdc_pol or usdc_sol for the lowest customer fees while still accepting stablecoins.
cURL Example with redirect_url
curl -X POST https://coinpayportal.com/api/payments/create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"business_id": "business-123",
"amount_usd": 100.00,
"currency": "usdc_pol",
"redirect_url": "https://yoursite.com/order/success?id=12345"
}'Response
{
"success": true,
"payment": {
"id": "payment-456",
"business_id": "business-123",
"amount_usd": "100.00",
"amount_crypto": "100.50",
"currency": "usdc_pol",
"payment_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"status": "pending",
"metadata": {
"network_fee_usd": 0.50,
"total_amount_usd": 100.50,
"redirect_url": "https://yoursite.com/order/success?id=12345"
},
"created_at": "2024-01-01T12:00:00Z",
"expires_at": "2024-01-01T12:15:00Z"
}
}Auto-Redirect After Payment
When redirect_url is provided, customers are automatically redirected back to your site 5 seconds after payment completion. A "Return to Merchant" button is also shown for immediate redirect.
/api/payments/:idRetrieve payment details by ID.
Node.js Example
const response = await fetch('https://coinpayportal.com/api/payments/payment-456', {
headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const data = await response.json();
console.log(data.payment.status);/api/payments/:id/qrGet QR code image for payment address.
Usage
<img src="https://coinpayportal.com/api/payments/payment-456/qr"
alt="Payment QR Code" />Business Collection
Business Collection payments allow the platform to collect payments from business users (subscription fees, service charges, etc.) with 100% forwarding to platform wallets.
Key Difference: Unlike regular payments (99.5% merchant / 0.5% platform), Business Collection forwards 100% of funds to the platform's collection wallet.
/api/business-collectionCreate a new business collection payment.
Request Body
{
"business_id": "business-123",
"amount": 99.99,
"currency": "USD",
"blockchain": "ETH", // BTC, BCH, ETH, POL, SOL
"description": "Monthly subscription fee",
"metadata": {
"plan": "premium",
"billing_period": "2024-01"
}
}Response
{
"success": true,
"payment": {
"id": "collection-456",
"payment_address": "0x1234...5678",
"amount": 99.99,
"currency": "USD",
"blockchain": "ETH",
"destination_wallet": "0xplatform...wallet",
"status": "pending",
"description": "Monthly subscription fee",
"expires_at": "2024-01-02T12:00:00Z",
"created_at": "2024-01-01T12:00:00Z"
}
}/api/business-collectionList business collection payments with optional filters.
Query Parameters
business_id - Filter by business (optional)
status - Filter by status: pending, confirmed, forwarded (optional)
limit - Results per page, default 50 (optional)
offset - Pagination offset (optional)
/api/business-collection/:idGet details of a specific business collection payment.
Collection Payment Statuses
pendingWaiting for payment
detectedPayment detected on blockchain
confirmedPayment confirmed
forwardingForwarding to platform wallet
forwarded100% forwarded to platform
expiredPayment request expired
Dashboard
/api/dashboard/statsGet payment statistics and recent activity.
Response
{
"success": true,
"stats": {
"total_payments": 150,
"successful_payments": 142,
"pending_payments": 5,
"failed_payments": 3,
"total_volume": "0.12345678",
"total_volume_usd": 5234.56
},
"recent_payments": [...]
}Settings
/api/settingsGet merchant notification settings.
/api/settingsUpdate notification preferences.
Request Body
{
"notifications_enabled": true,
"email_notifications": true,
"web_notifications": false
}Supported Cryptocurrencies
Native Cryptocurrencies
btcethpolsolUSDC Stablecoin (Multi-Chain)
Recommendation: Use usdc_pol or usdc_sol for the lowest fees while accepting stable USD-pegged payments.
Webhooks
Configure webhook URLs in your business settings to receive real-time payment notifications.
Webhook Events
payment.confirmedPayment confirmed on blockchain — safe to fulfill order
payment.forwardedFunds forwarded to your merchant wallet
payment.expiredPayment request expired (15 minute window)
escrow.createdNew escrow created
escrow.fundedEscrow deposit detected on-chain
escrow.releasedDepositor released funds to beneficiary
escrow.settledFunds forwarded on-chain to beneficiary
escrow.refundedFunds returned to depositor
escrow.disputedDispute opened on escrow
test.webhookTest webhook (sent from dashboard)
Payload Structure
All webhook events use this SDK-compliant nested structure:
| Field | Type | Description |
|---|---|---|
id | string | Unique event ID (evt_paymentId_timestamp) |
type | string | Event type (payment.confirmed, etc.) |
data | object | Event data (see below) |
created_at | string | ISO 8601 timestamp |
business_id | string | Your business ID |
Data Object Fields
| Field | Type | Description |
|---|---|---|
payment_id | string | Payment identifier |
status | string | Payment status |
amount_crypto | string | Amount in crypto |
amount_usd | string | Amount in USD |
currency | string | Blockchain (ETH, BTC, etc.) |
payment_address | string | Payment address |
tx_hash | string | Transaction hash (when available) |
metadata | object | Custom data passed during payment creation (order_id, customer info, etc.) |
Webhook Headers
Content-Type: application/json X-CoinPay-Signature: t=1702234567,v1=5d41402abc4b2a76b9719d911017c592 User-Agent: CoinPay-Webhook/1.0
The X-CoinPay-Signature header contains: t (Unix timestamp) and v1 (HMAC-SHA256 signature)
payment.confirmed Payload
Sent when payment is confirmed. Safe to fulfill the order.
{
"id": "evt_pay_abc123_1705315800",
"type": "payment.confirmed",
"data": {
"payment_id": "pay_abc123",
"status": "confirmed",
"amount_crypto": "0.05",
"amount_usd": "150.00",
"currency": "ETH",
"payment_address": "0x1234...5678",
"tx_hash": "0xabc...def",
"received_amount": "0.05",
"confirmed_at": "2024-01-15T10:30:00Z",
"metadata": {
"order_id": "order_12345",
"customer_email": "customer@example.com"
}
},
"created_at": "2024-01-15T10:30:00Z",
"business_id": "biz_xyz789"
}payment.forwarded Payload
Sent when funds are forwarded to your wallet. Includes transaction hashes.
{
"id": "evt_pay_abc123_1705316100",
"type": "payment.forwarded",
"data": {
"payment_id": "pay_abc123",
"status": "forwarded",
"amount_crypto": "0.05",
"amount_usd": "150.00",
"currency": "ETH",
"merchant_amount": 0.049,
"platform_fee": 0.001,
"tx_hash": "0xmerchant123...",
"merchant_tx_hash": "0xmerchant123...",
"platform_tx_hash": "0xplatform456...",
"metadata": {
"order_id": "order_12345",
"customer_email": "customer@example.com"
}
},
"created_at": "2024-01-15T10:35:00Z",
"business_id": "biz_xyz789"
}payment.expired Payload
Sent when payment expires without receiving funds.
{
"id": "evt_pay_abc123_1705316700",
"type": "payment.expired",
"data": {
"payment_id": "pay_abc123",
"status": "expired",
"amount_crypto": "0.05",
"amount_usd": "150.00",
"currency": "ETH",
"reason": "Payment window expired (15 minutes)",
"expired_at": "2024-01-15T10:45:00Z"
},
"created_at": "2024-01-15T10:45:00Z",
"business_id": "biz_xyz789"
}Verifying Webhook Signatures
The signature is computed as HMAC-SHA256(timestamp.rawBody, secret) where rawBody is the exact JSON string received.
JavaScript/Node.js Example
import crypto from 'crypto';
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
// Parse signature header (format: t=timestamp,v1=signature)
const parts = signatureHeader.split(',');
const signatureParts = {};
for (const part of parts) {
const [key, value] = part.split('=');
signatureParts[key] = value;
}
const timestamp = signatureParts.t;
const expectedSignature = signatureParts.v1;
// Check timestamp tolerance (300 seconds)
const timestampAge = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (Math.abs(timestampAge) > 300) {
return false; // Reject old webhooks
}
// Compute expected signature using raw body string
const signedPayload = `${timestamp}.${rawBody}`;
const computedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(computedSignature, 'hex')
);
}
// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString();
const signature = req.headers['x-coinpay-signature'];
if (!verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(rawBody);
// Handle the event
switch (event.type) {
case 'payment.confirmed':
// Fulfill the order
console.log('Payment confirmed:', event.data.payment_id);
break;
case 'payment.forwarded':
// Funds forwarded to your wallet
console.log('Funds forwarded:', event.data.merchant_tx_hash);
break;
}
res.json({ received: true });
});Important: Always use the raw request body string for signature verification. Do not parse and re-stringify the JSON, as whitespace differences will cause signature mismatches. Use express.raw() or equivalent middleware.
SDK Support: Use verifyWebhookSignature() and parseWebhookPayload() from the CoinPay SDK for easier integration.
Rate Limits & Fees
Rate Limits
• API Requests: 100 requests/minute
• Payment Creation: 10 payments/minute
• Webhook Retries: 3 attempts with exponential backoff
Platform Fees
• Starter (free): 1% per transaction & escrow release
• Professional ($49/mo): 0.5% per transaction & escrow release
• Escrow Refunds: No fee (full amount returned)
• Network Fees: Paid by customer
• Minimum Payment: $1.00 USD
Reputation & DID
Decentralized reputation system for agents and users. Track task completion, issue verifiable credentials, and query reputation scores — all anchored to DIDs (Decentralized Identifiers).
How Reputation Works
- Claim a DID — Each agent/user claims a unique decentralized identifier.
- Complete Tasks — After escrow settlement, submit a task receipt.
- Build Reputation — Receipts are aggregated into a reputation score.
- Verify Credentials — Anyone can verify credentials and check reputation.
Account Registration
/api/auth/registerCreate a new merchant account.
Request
POST /api/auth/register
Content-Type: application/json
{
"email": "agent@example.com",
"password": "securepassword",
"name": "My Agent"
}Response (201)
{
"success": true,
"merchant": { "id": "uuid", "email": "agent@example.com" },
"token": "jwt_token_here"
}/api/auth/loginAuthenticate and get a JWT token.
Request
POST /api/auth/login
Content-Type: application/json
{
"email": "agent@example.com",
"password": "securepassword"
}Response (200)
{
"success": true,
"merchant": { "id": "uuid", "email": "agent@example.com" },
"token": "jwt_token_here"
}DID Management
/api/reputation/did/claimClaim a new DID for the authenticated user/agent.
Request
POST /api/reputation/did/claim
Authorization: Bearer <token>
Content-Type: application/json
{
"displayName": "Agent Smith"
}Response
{
"success": true,
"did": "did:coinpay:abc123...",
"displayName": "Agent Smith",
"createdAt": "2026-01-15T10:00:00Z"
}/api/reputation/did/meGet the DID associated with the current authenticated user.
Request
GET /api/reputation/did/me Authorization: Bearer <token>
Response
{
"success": true,
"did": "did:coinpay:abc123...",
"displayName": "Agent Smith",
"linkedDids": [],
"createdAt": "2026-01-15T10:00:00Z"
}Receipts & Reputation
/api/reputation/receiptSubmit a task receipt after escrow settlement. This contributes to the agent's reputation score.
Request
POST /api/reputation/receipt
Authorization: Bearer <token>
Content-Type: application/json
{
"escrowId": "esc_abc123",
"taskDescription": "Frontend bug fix",
"rating": 5,
"counterpartyDid": "did:coinpay:xyz789..."
}Response
{
"success": true,
"receiptId": "rcp_def456",
"credentialId": "cred_ghi789",
"reputationDelta": "+0.5"
}/api/reputation/agent/[did]/reputationQuery the reputation score for a specific DID.
Request
GET /api/reputation/agent/did:coinpay:abc123.../reputation
Response
{
"success": true,
"did": "did:coinpay:abc123...",
"score": 4.8,
"totalTasks": 42,
"successRate": 0.95,
"credentials": 38,
"lastActive": "2026-02-10T14:30:00Z"
}/api/reputation/receipts?did=[did]List all task receipts for a DID.
Request
GET /api/reputation/receipts?did=did:key:z6Mk... Authorization: Bearer <token>
Response
{
"success": true,
"receipts": [
{
"receipt_id": "rcp_def456",
"agent_did": "did:key:z6Mk...",
"buyer_did": "did:key:z6Mk...",
"amount": 500,
"currency": "USD",
"outcome": "accepted",
"category": "development",
"created_at": "2026-02-10T14:30:00Z"
}
]
}Verifiable Credentials
/api/reputation/credentials?did=[did]List all credentials issued to a DID.
Request
GET /api/reputation/credentials?did=did:key:z6Mk...
Response
{
"success": true,
"credentials": [
{
"id": "cred_ghi789",
"credential_type": "TaskCompletionCredential",
"issuer_did": "did:web:coinpayportal.com",
"revoked": false,
"issued_at": "2026-02-10T14:30:00Z"
}
]
}/api/reputation/credential/[id]Retrieve a verifiable credential by ID.
Request
GET /api/reputation/credential/cred_ghi789
Response
{
"success": true,
"credential": {
"id": "cred_ghi789",
"type": "TaskCompletionCredential",
"issuer": "did:coinpay:system",
"subject": "did:coinpay:abc123...",
"issuanceDate": "2026-02-10T14:30:00Z",
"proof": { "type": "Ed25519Signature2020", "...": "..." }
}
}/api/reputation/verifyVerify a verifiable credential's authenticity and revocation status.
Request
POST /api/reputation/verify
Content-Type: application/json
{
"credentialId": "cred_ghi789"
}Response
{
"success": true,
"valid": true,
"revoked": false,
"issuer": "did:coinpay:system",
"expiresAt": null
}/api/reputation/revocation-listGet the current credential revocation list.
Request
GET /api/reputation/revocation-list
Response
{
"success": true,
"revocations": [
{ "credentialId": "cred_revoked1", "revokedAt": "2026-01-20T08:00:00Z", "reason": "dispute" }
],
"updatedAt": "2026-02-13T00:00:00Z"
}Reputation Badge
/api/reputation/badge/[did]Get an embeddable SVG reputation badge (shields.io style). Returns an SVG image showing acceptance rate and task count. Green for good, yellow for moderate, red for poor.
Usage
<!-- Embed in HTML --> <img src="https://coinpayportal.com/api/reputation/badge/did:key:z6Mk..." alt="Reputation" /> <!-- Embed in Markdown (GitHub README, etc.) -->  <!-- Get badge URL via CLI --> coinpay reputation badge did:key:z6Mk...
Trust Vector (CPTL v2)
7-Dimension Trust Vector
Phase 2 introduces a multi-dimensional trust vector computed from categorized action receipts. The reputation endpoint now returns both legacy windows AND a trust vector.
- E — Economic Score (from economic.* actions, log-scaled by USD value)
- P — Productivity Score (from productivity.* actions)
- B — Behavioral Score (dispute rate, response patterns)
- D — Diversity Score (log of unique counterparties)
- R — Recency Score (exponential decay, 90-day half-life)
- A — Anomaly Penalty (from anti-gaming flags)
- C — Compliance Penalty (from compliance.* actions)
Trust Vector Dimensions — Detailed
Measures the total value and frequency of completed financial transactions. Higher scores indicate more economic activity with successful payment completions and fewer refunds.
Tracks task and project completion rates across platforms. Includes gigs completed, applications accepted, posts created, and other productive actions submitted through platform integrations.
Reflects dispute history and behavioral patterns. A high score means few disputes relative to completed transactions. Repeated disputes or chargebacks will lower this score significantly.
Measures how many unique counterparties (buyers, sellers, platforms) you've transacted with. Higher diversity indicates a broader, more trustworthy reputation that isn't dependent on a single relationship.
A time-decay multiplier that weights recent activity more heavily. Activity within the last 90 days contributes fully, while older activity gradually decays. Staying active keeps this score high.
Penalty applied when suspicious patterns are detected, such as rapid self-dealing, wash trading, or artificial volume inflation. A score of 0 means no anomalies detected. Negative values indicate active penalties.
Penalty for compliance violations such as terms-of-service breaches, reported incidents, or platform rule violations. A score of 0 means a clean compliance record. Negative values indicate active penalties.
Window Stats — Detailed
Reputation is computed over rolling time windows (7d, 30d, 90d, lifetime). Each window shows these stats:
Total number of reputation receipts (transactions, tasks, social actions) recorded in this time window.
Percentage of tasks/transactions that were accepted or completed successfully without disputes.
Percentage of tasks/transactions that resulted in a dispute. Lower is better — high dispute rates reduce your Behavioral (B) trust score.
Total USD value of all transactions in this window. Economic (E) trust score is log-scaled from this value.
Average USD value per transaction in this window. Helps distinguish between many small transactions vs. fewer high-value ones.
Number of distinct counterparties in this window. Directly feeds the Diversity (D) trust dimension — more unique counterparties = higher D score.
Action Categories
Receipts now accept action_category and action_type fields. Valid categories:
Canonical Categories
economic.transaction (weight: 10) economic.dispute (weight: -12) economic.refund productivity.task productivity.application (weight: 1) productivity.completion (weight: 5) identity.profile_update (weight: 0.5) identity.verification (weight: 3) social.post (weight: 0.05) social.comment (weight: 0.02) social.endorsement compliance.incident compliance.violation (weight: -20)
/api/reputation/receiptSubmit an action receipt (Phase 2). Now accepts action_category and action_type fields.
Request (Phase 2)
POST /api/reputation/receipt
Authorization: Bearer <token>
Content-Type: application/json
{
"receipt_id": "550e8400-...",
"task_id": "550e8400-...",
"agent_did": "did:key:z6Mk...",
"buyer_did": "did:key:z6Mk...",
"action_category": "productivity.completion",
"action_type": "code_review",
"amount": 250,
"currency": "USD",
"outcome": "accepted",
"signatures": { "escrow_sig": "..." }
}/api/reputation/agent/[did]/reputationNow returns trust_vector alongside legacy windows.
Response (Phase 2)
{
"success": true,
"reputation": { "windows": { ... }, "anti_gaming": { ... } },
"trust_vector": {
"E": 42.5,
"P": 12.3,
"B": 9.1,
"D": 2.08,
"R": 0.87,
"A": 0,
"C": 0
},
"computed_at": "2026-02-13T..."
}Error Codes
Error Response Format
{
"success": false,
"error": "Detailed error message here"
}