Skip to content
Back to Home

API Documentation

Complete reference for the CoinPay REST API

📦 Node.js SDK & CLI

Use our official SDK for seamless integration with your Node.js applications

View SDK Docs

🔐 Non-Custodial Web Wallet

Multi-chain wallet for humans and AI agents — no signup, no KYC, API-first

View Docs

🔐 Escrow Service

Trustless crypto escrow — hold funds until both sides are satisfied. Token-based auth, no accounts needed.

View Docs

💳 Credit Card Payments

Accept credit & debit cards via Stripe Connect — gateway mode, escrow mode, and automatic merchant onboarding

View Docs

⚡ x402 Payment Protocol

HTTP-native machine payments — the only multi-chain x402 facilitator. BTC, ETH, SOL, USDC, Lightning & more.

x402 Dashboard

🛡️ Reputation & DID

Decentralized reputation system — track agent performance, issue verifiable credentials, query trust scores

View Docs

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

  1. Client requests a paid API endpoint (e.g. GET /api/premium)
  2. Server returns 402 with an accepts[] array listing all payment methods + prices
  3. Client picks a method (BTC, USDC, Lightning, card...) and creates a payment proof
  4. Client retries the request with an X-Payment header containing the proof
  5. 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

MethodHow to PayProof
USDC (EVM)Sign an EIP-712 typed message authorizing transferFrom — gasless, no on-chain tx until settlementsignature + authorization object
Bitcoin / BCHBroadcast a transaction to the merchant's payTo addressTransaction ID (txid)
LightningPay the BOLT12 offer via any Lightning walletPayment preimage
SolanaSign and broadcast a SOL/USDC transferTransaction 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:

PlanCommissionMerchant 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.

POST/api/x402/verify

Verify 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"
}
POST/api/x402/settle

Settle (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.

🔐 Non-Custodial

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

BTC

Bitcoin

BCH

Bitcoin Cash

ETH

Ethereum

POL

Polygon

SOL

Solana

USDC_ETH

USDC (Ethereum)

USDC_POL

USDC (Polygon)

USDC_SOL

USDC (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 ETH

List or filter addresses

coinpay-wallet send
<wallet-id> --from <addr> --to <addr> --chain ETH --amount 0.5

Send a transaction

coinpay-wallet history
<wallet-id> --chain BTC --limit 20

View transaction history

coinpay-wallet sync
<wallet-id> --chain SOL

Sync 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)

  1. Client requests a challenge: GET /api/web-wallet/auth/challenge?wallet_id=UUID
  2. Server returns a random challenge string with expiration
  3. Client signs the challenge with their private key
  4. Client submits signature: POST /api/web-wallet/auth/verify
  5. 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

🔄 New Chains Added?

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
#   ...
POST/api/web-wallet/create

Register 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 }
    ]
  }
}
POST/api/web-wallet/import

Import 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" }
  ]
}
GET/api/web-wallet/auth/challenge

Request 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"
  }
}
POST/api/web-wallet/auth/verify

Verify 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..."
  }
}
GET/api/web-wallet/:id

Get wallet info. Requires wallet JWT.

POST/api/web-wallet/:id/derive

Derive 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"
}
GET/api/web-wallet/:id/addresses

List all addresses. Optional filters: chain, active_only.

DELETE/api/web-wallet/:id/addresses/:address_id

Deactivate an address (soft delete).

GET/api/web-wallet/:id/balances

Get 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" }
    ]
  }
}
GET/api/web-wallet/:id/balances/total-usd

Get total wallet balance converted to USD with per-chain breakdown.

POST/api/web-wallet/:id/prepare-tx

Prepare 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

POST/api/web-wallet/:id/broadcast

Broadcast 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..."
  }
}
POST/api/web-wallet/:id/estimate-fee

Get 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 }
    }
  }
}
GET/api/web-wallet/:id/transactions

Get 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

GET/api/web-wallet/:id/transactions/:tx_id

Get details of a specific transaction.

POST/api/web-wallet/:id/sync-history

Sync 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".

GET/api/web-wallet/:id/settings

Get wallet security settings (spend limits, whitelist, confirmation delay).

PATCH/api/web-wallet/:id/settings

Update 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
}
POST/api/web-wallet/:id/webhooks

Register a webhook for wallet events.

Request Body

{
  "url": "https://myapp.com/webhooks/wallet",
  "events": ["transaction.incoming", "transaction.confirmed"]
}

Available Events

transaction.incoming

New incoming transaction detected

transaction.confirmed

Transaction reached confirmation threshold

transaction.outgoing

Outgoing transaction broadcast

balance.changed

Balance updated

GET/api/web-wallet/:id/webhooks

List registered webhooks.

DELETE/api/web-wallet/:id/webhooks/:webhook_id

Remove a webhook registration.

Rate Limits

Wallet creation5/hour per IP
Auth challenge/verify10/min per IP
Balance queries60/min per IP
Transaction prep20/min per IP
Broadcast10/min per IP
Fee estimation60/min per IP
Sync history10/min per IP
Settings30/min per IP

Try the Web Wallet

Create a wallet in seconds — no signup required.

Open Wallet

🤖 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

  1. Create — Specify chain, amount, depositor & beneficiary addresses. Get a deposit address + two auth tokens. Share the Escrow ID!
  2. Fund — Depositor sends crypto to the escrow address. Auto-detected by the balance monitor.
  3. Manage — Both parties can manage the escrow via /escrow/manage?id=xxx&token=yyy with shareable links generated at creation.
  4. Release or Dispute — Depositor releases funds (using release_token) or either party opens a dispute.
  5. 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

pending

Awaiting deposit

funded

Deposit received on-chain

released

Depositor released funds

settled

Funds forwarded to beneficiary

disputed

Dispute opened by either party

refunded

Funds returned to depositor

expired

Deposit window expired

POST/api/escrow

Create 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

GET/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
}
GET/api/escrow/:id

Get escrow details by ID. Public endpoint — no auth required.

cURL Example

curl https://coinpayportal.com/api/escrow/a1b2c3d4-...
POST/api/escrow/:id/auth

Authenticate 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.

POST/api/escrow/:id/release

Release 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.

POST/api/escrow/:id/refund

Request 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..."
}
POST/api/escrow/:id/dispute

Open 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.

GET/api/escrow/:id/events

Get 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

  1. Create Series — Define the amount, interval (weekly/biweekly/monthly), payment method, and beneficiary.
  2. Auto-Charge — The payment monitor daemon automatically creates and funds a new escrow each period. No cron needed.
  3. Individual Release — The merchant reviews and releases each escrow individually when work is delivered.
  4. 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

active

Series is running, auto-charges each period

paused

Temporarily stopped, can resume

completed

All periods fulfilled (max_periods reached)

cancelled

Permanently stopped by user

POST/api/escrow/series

Create 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"
}
GET/api/escrow/series?business_id=uuid&status=active

List 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
}
GET/api/escrow/series/:id

Get 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 }
  ]
}
PATCH/api/escrow/series/:id

Update a series — pause, resume, or change amount.

Request Body

{
  "status": "paused",    // "paused" or "active" (to resume)
  "amount": 600          // Optional: change amount for future periods
}
DELETE/api/escrow/series/:id

Cancel 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.

GET/api/rates

Get 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

USD
EUR
GBP
CAD
AUD
JPY
CHF
CNY
INR
BRL

Single 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.

POST/api/auth/register

Create 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
POST/api/auth/login

Login 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"
  }
}
GET/api/auth/me

Get 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

Starter (Free)
  • • Up to 100 transactions/month
  • • All supported chains
  • • Basic API access
  • • Email support
Professional ($49/month)
  • • Unlimited transactions
  • • Priority support
  • • Advanced analytics
  • • Custom webhooks
GET/api/subscription-plans

Get 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": { ... }
    }
  ]
}
GET/api/entitlements

Get 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"
  }
}
POST/api/subscriptions/checkout

Create 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..."
}
GET/api/subscriptions/status

Get 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
  }
}
DELETE/api/subscriptions/status

Cancel 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

429
Transaction Limit Exceeded
Monthly transaction limit reached. Upgrade to Professional for unlimited.
403
Feature Not Available
Feature not available on current plan. Upgrade required.
402
Subscription Inactive
Subscription is past_due or cancelled. Payment required.

Businesses

GET/api/businesses

List all businesses for the authenticated merchant.

cURL Example

curl https://coinpayportal.com/api/businesses \
  -H "Authorization: Bearer YOUR_TOKEN"
POST/api/businesses

Create 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();
PATCH/api/businesses/:id

Update an existing business.

DELETE/api/businesses/:id

Delete 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.

GET/api/supported-coins

Get 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

BTC

Bitcoin

BCH

Bitcoin Cash

ETH

Ethereum

POL

Polygon

SOL

Solana

USDT

Tether

USDC

USD Coin

BNB

BNB

XRP

XRP

ADA

Cardano

DOGE

Dogecoin

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

POST/api/payments/create

Create 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.

GET/api/payments/:id

Retrieve 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);
GET/api/payments/:id/qr

Get 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.

POST/api/business-collection

Create 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"
  }
}
GET/api/business-collection

List 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)

GET/api/business-collection/:id

Get details of a specific business collection payment.

Collection Payment Statuses

pending

Waiting for payment

detected

Payment detected on blockchain

confirmed

Payment confirmed

forwarding

Forwarding to platform wallet

forwarded

100% forwarded to platform

expired

Payment request expired

Dashboard

GET/api/dashboard/stats

Get 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

GET/api/settings

Get merchant notification settings.

PUT/api/settings

Update notification preferences.

Request Body

{
  "notifications_enabled": true,
  "email_notifications": true,
  "web_notifications": false
}

Supported Cryptocurrencies

Native Cryptocurrencies

Bitcoin
BTC
Code: btc
Confirmations: 3
Network Fee: ~$2-3
Ethereum
ETH
Code: eth
Confirmations: 12
Network Fee: ~$3-5
Polygon
POL
Code: pol
Confirmations: 128
Network Fee: ~$0.01
Solana
SOL
Code: sol
Confirmations: 32
Network Fee: ~$0.001

USDC Stablecoin (Multi-Chain)

USDC on Polygon
Low Fee
usdc_pol
Network Fee: ~$0.01
USDC on Solana
Low Fee
usdc_sol
Network Fee: ~$0.001
USDC on Ethereum
usdc_eth
Network Fee: ~$3-5

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.confirmed

Payment confirmed on blockchain — safe to fulfill order

payment.forwarded

Funds forwarded to your merchant wallet

payment.expired

Payment request expired (15 minute window)

escrow.created

New escrow created

escrow.funded

Escrow deposit detected on-chain

escrow.released

Depositor released funds to beneficiary

escrow.settled

Funds forwarded on-chain to beneficiary

escrow.refunded

Funds returned to depositor

escrow.disputed

Dispute opened on escrow

test.webhook

Test webhook (sent from dashboard)

Payload Structure

All webhook events use this SDK-compliant nested structure:

FieldTypeDescription
idstringUnique event ID (evt_paymentId_timestamp)
typestringEvent type (payment.confirmed, etc.)
dataobjectEvent data (see below)
created_atstringISO 8601 timestamp
business_idstringYour business ID

Data Object Fields

FieldTypeDescription
payment_idstringPayment identifier
statusstringPayment status
amount_cryptostringAmount in crypto
amount_usdstringAmount in USD
currencystringBlockchain (ETH, BTC, etc.)
payment_addressstringPayment address
tx_hashstringTransaction hash (when available)
metadataobjectCustom 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

  1. Claim a DID — Each agent/user claims a unique decentralized identifier.
  2. Complete Tasks — After escrow settlement, submit a task receipt.
  3. Build Reputation — Receipts are aggregated into a reputation score.
  4. Verify Credentials — Anyone can verify credentials and check reputation.

Account Registration

POST/api/auth/register

Create 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"
}
POST/api/auth/login

Authenticate 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

POST/api/reputation/did/claim

Claim 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"
}
GET/api/reputation/did/me

Get 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

POST/api/reputation/receipt

Submit 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"
}
GET/api/reputation/agent/[did]/reputation

Query 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"
}
GET/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

GET/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"
    }
  ]
}
GET/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", "...": "..." }
  }
}
POST/api/reputation/verify

Verify 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
}
GET/api/reputation/revocation-list

Get 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

GET/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.) -->
![Reputation](https://coinpayportal.com/api/reputation/badge/did:key:z6Mk...)

<!-- 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

EEconomic

Measures the total value and frequency of completed financial transactions. Higher scores indicate more economic activity with successful payment completions and fewer refunds.

PProductivity

Tracks task and project completion rates across platforms. Includes gigs completed, applications accepted, posts created, and other productive actions submitted through platform integrations.

BBehavioral

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.

DDiversity

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.

RRecency

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.

AAnomaly Penalty

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.

CCompliance Penalty

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:

Tasks

Total number of reputation receipts (transactions, tasks, social actions) recorded in this time window.

Accepted Rate

Percentage of tasks/transactions that were accepted or completed successfully without disputes.

Dispute Rate

Percentage of tasks/transactions that resulted in a dispute. Lower is better — high dispute rates reduce your Behavioral (B) trust score.

Volume

Total USD value of all transactions in this window. Economic (E) trust score is log-scaled from this value.

Avg Value

Average USD value per transaction in this window. Helps distinguish between many small transactions vs. fewer high-value ones.

Unique Buyers

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)
POST/api/reputation/receipt

Submit 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": "..." }
}
GET/api/reputation/agent/[did]/reputation

Now 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

400
Bad Request
Invalid request parameters or missing required fields
401
Unauthorized
Invalid or missing authentication token
402
Payment Required
Subscription inactive or payment needed
403
Forbidden
Feature not available on current plan
404
Not Found
Resource not found
429
Too Many Requests
Rate limit or transaction limit exceeded
500
Server Error
Internal server error - please try again

Error Response Format

{
  "success": false,
  "error": "Detailed error message here"
}