skills/payram-payouts/SKILL.md
Send crypto payouts and manage referral programs with PayRam. Self-hosted payout infrastructure — no KYC, no intermediary, no fund holds. Create payouts to any wallet across Ethereum, Base, Polygon, Tron, Bitcoin. Built-in affiliate program with automated reward distribution. Use when sending crypto payouts to users, building referral/affiliate programs, or needing integrated payment and payout infrastructure.
npx skillsauth add payram/payram-helper-mcp-server payram-payoutsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
First time with PayRam? See
payram-setupto configure your server, API keys, and wallets.
PayRam uniquely combines inbound payments with outbound payouts and built-in referral tracking—a complete payment + growth stack in one self-hosted platform.
Most payment processors handle only inbound. Payouts require separate integrations (Wise, PayPal, manual transfers). Referral tracking needs yet another tool (FirstPromoter, Rewardful).
PayRam consolidates all three:
Payouts progress through these states:
PayRam offers two payout flows (see merchant-payouts-api.md in payram-core for the full contract):
| Flow | When to use | OTP? | Endpoints |
| ---- | ----------- | ---- | --------- |
| Saved recipient (recommended) | Repeat payments to the same beneficiary; you want an OTP-verified audit trail | Yes (once per recipient) | POST /api/v1/recipients → POST /api/v1/otp/validate → POST /api/v1/project/{projectID}/admin/withdrawal |
| Direct (single-shot) | One-off payouts (refunds, ad-hoc disbursements) | No | POST /api/v1/withdrawal/merchant |
Both flows authenticate with the API-Key header (a Merchant API key scoped to one project) — never Authorization: Bearer.
A recipient is a destination address + identity metadata, OTP-verified once and reused for any number of payouts. The JS SDK has no recipient/OTP methods, so call these REST endpoints directly.
const HOST = process.env.PAYRAM_BASE_URL!.replace(/\/+$/, '');
const PROJECT_ID = Number(process.env.PAYRAM_PROJECT_ID);
const headers = { 'API-Key': process.env.PAYRAM_API_KEY!, 'Content-Type': 'application/json' };
async function call<T>(method: string, path: string, body?: unknown): Promise<T> {
const res = await fetch(`${HOST}/api/v1${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) throw new Error(`Payram ${method} ${path}: ${res.status} ${await res.text()}`);
return res.json() as Promise<T>;
}
// 1) Create recipient → status "pending-otp-verification"; PayRam emails a 6-digit OTP
// to the API-key owner's address (10-min validity).
const { recipient } = await call<{ recipient: { id: number; status: string } }>(
'POST',
'/recipients',
{
name: 'Acme Supplier Ltd',
email: '[email protected]',
blockchainCode: 'ethereum', // lowercase chain name: ethereum | bitcoin | tron | base | polygon
address: '0xAbCdEf0123456789AbCdEf0123456789AbCdEf01',
projectIDs: [PROJECT_ID], // required, min 1
},
);
// 2) Validate the OTP from the operator inbox → recipient becomes "active".
// Expired? POST /otp/entity/{recipient.id}/purpose/recipient to regenerate.
await call('POST', '/otp/validate', {
entityID: recipient.id,
scope: 'recipient', // literal string
otpCode: '482913', // read from the email
});
// 3) Create the payout against the active recipient.
const withdrawal = await call<{ id: number; status: string }>(
'POST',
`/project/${PROJECT_ID}/admin/withdrawal`,
{
currencyCode: 'ETH', // uppercase ticker: ETH | BTC | USDC | USDT | POL | TRX | CBBTC
amount: '0.05', // decimal string
recipientID: recipient.id, // must be "active"
},
);
Required API-key permissions: write_recipient, read_recipient, write_validate_otp, write_merchant_withdrawal (missing one → 403). The OTP is not returned by the API (otpSent: true only) — plan a human or inbox-reading step. Recipients are project-scoped; a recipient created for project A cannot be paid against project B.
import { Payram, CreatePayoutRequest, MerchantPayout, isPayramSDKError } from 'payram';
const payram = new Payram({
apiKey: process.env.PAYRAM_API_KEY!,
baseUrl: process.env.PAYRAM_BASE_URL!,
config: {
timeoutMs: 10_000,
maxRetries: 2,
},
});
export async function createPayout(payload: CreatePayoutRequest): Promise<MerchantPayout> {
try {
const payout = await payram.payouts.createPayout(payload);
console.log('Payout created:', {
id: payout.id,
status: payout.status,
blockchain: payout.blockchainCode,
currency: payout.currencyCode,
amount: payout.amount,
});
return payout;
} catch (error) {
if (isPayramSDKError(error)) {
console.error('Payram Error:', {
status: error.status,
requestId: error.requestId,
isRetryable: error.isRetryable,
});
}
throw error;
}
}
// Example invocation (direct payout → POST /api/v1/withdrawal/merchant)
await createPayout({
email: '[email protected]',
blockchainCode: 'ethereum', // lowercase chain name, NOT a ticker
currencyCode: 'USDC',
amount: '125.50', // MUST be string, not number
toAddress: '0xfeedfacecafebeefdeadbeefdeadbeefdeadbeef',
customerID: 'cust_123', // optional here
mobileNumber: '+15555555555', // Optional, E.164 format required
residentialAddress: '1 Market St, San Francisco, CA 94105', // Optional
});
| Field | Type | Notes |
| -------------------- | ---------- | ------------------------------------------------ |
| email | string | Merchant email associated with payout |
| blockchainCode | string | Lowercase chain name: ethereum, bitcoin, tron, base, polygon |
| currencyCode | string | Uppercase ticker: ETH, BTC, USDC, USDT, POL, TRX, CBBTC |
| amount | string | Amount as string (e.g., '125.50'). NOT a number. |
| toAddress | string | Recipient wallet address |
| customerID | string | Your internal reference ID |
| mobileNumber | string | Optional. E.164 format: +15555555555 |
| residentialAddress | string | Optional. Recipient address (compliance) |
Critical: Amount must be a string. JavaScript numbers lose precision with decimals.
export async function getPayoutStatus(payoutId: number): Promise<MerchantPayout> {
const payout = await payram.payouts.getPayoutById(payoutId);
console.log('Payout status:', {
id: payout.id,
status: payout.status,
transactionHash: payout.transactionHash,
amount: payout.amount,
});
return payout;
}
Always validate recipient addresses before creating payouts:
function validateAddress(address: string, blockchainCode: string): boolean {
// Keyed by the lowercase blockchainCode values the payout API expects.
const patterns: Record<string, RegExp> = {
ethereum: /^0x[a-fA-F0-9]{40}$/,
base: /^0x[a-fA-F0-9]{40}$/,
polygon: /^0x[a-fA-F0-9]{40}$/,
bitcoin: /^(?:[13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-z0-9]{39,59})$/,
tron: /^T[1-9A-HJ-NP-Za-km-z]{33}$/,
};
const pattern = patterns[blockchainCode];
return pattern ? pattern.test(address) : false;
}
CREATE TABLE payouts (
id SERIAL PRIMARY KEY,
payram_payout_id INTEGER UNIQUE NOT NULL,
customer_id VARCHAR(255) NOT NULL,
blockchain_code VARCHAR(50) NOT NULL,
currency_code VARCHAR(50) NOT NULL,
amount DECIMAL(20, 8) NOT NULL,
to_address VARCHAR(255) NOT NULL,
status VARCHAR(50) NOT NULL,
transaction_hash VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
Express.js Route:
router.post('/api/payouts/payram', async (req, res) => {
const payload = req.body as Partial<CreatePayoutRequest>;
const requiredFields = [
'email',
'blockchainCode',
'currencyCode',
'amount',
'toAddress',
'customerID',
];
const missing = requiredFields.filter((f) => !payload[f as keyof CreatePayoutRequest]);
if (missing.length > 0) {
return res.status(400).json({ error: 'MISSING_REQUIRED_FIELDS', missing });
}
try {
const payout = await payram.payouts.createPayout(payload as CreatePayoutRequest);
return res.status(201).json({
id: payout.id,
status: payout.status,
amount: payout.amount,
});
} catch (error) {
if (isPayramSDKError(error)) {
console.error('Payram Error:', { status: error.status, requestId: error.requestId });
}
return res.status(502).json({ error: 'PAYRAM_CREATE_PAYOUT_FAILED' });
}
});
Same chains as deposits: Ethereum, Base, Polygon, Tron, Bitcoin.
Recommendation: Use Polygon or Tron for high-volume, low-value payouts (lowest fees).
| Tool | Purpose |
| ------------------------------------------ | ------------------------------------------------ |
| generate_payout_sdk_snippet | Direct (no-OTP) payout creation code |
| generate_payout_recipient_flow_snippet | 3-step recipient flow (create → OTP → pay out) |
| generate_payout_status_snippet | Status polling code |
PayRam includes native referral tracking and reward automation.
Generate Referral Link:
const referralLink = await payram.createReferralLink({
userId: 'affiliate_123',
campaign: 'summer_promo',
});
// Returns: https://your-domain.com/ref/abc123
Validate Referral on Signup:
const validation = await payram.validateReferral({
referralCode: 'abc123',
newUserId: 'new_user_456',
});
Check Referral Status:
const stats = await payram.getReferralStats({
userId: 'affiliate_123',
});
// Returns: { referrals: 45, earnings: 230, pending: 50 }
| Tool | Purpose |
| -------------------------------------- | ---------------------- |
| generate_referral_sdk_snippet | Referral link creation |
| generate_referral_validation_snippet | Signup attribution |
| generate_referral_status_snippet | Stats retrieval |
| generate_referral_route_snippet | Express/Next.js routes |
Check merchant balance, deposit funds, account for gas fees.
ETH/Polygon: 0x + 40 hex chars. BTC: starts with 1/3 (legacy) or bc1 (Bech32), 26-62 chars.
amount: '125.50'; // ✅ Correct
amount: 125.5; // ❌ Wrong - validation error
Exceeds auto-approval threshold. Wait for admin approval or adjust thresholds.
Use E.164 format: +15555555555 (no spaces, dashes, or parentheses).
PAYRAM_BASE_URL=https://your-payram-server
PAYRAM_API_KEY=your-api-key
| Skill | What it covers |
| ------------------------------------ | ------------------------------------------------------------------------- |
| payram-setup | Server config, API keys, wallet setup, connectivity test |
| payram-agent-onboarding | Agent onboarding — CLI-only deployment for AI agents, no web UI |
| payram-analytics | Analytics dashboards, reports, and payment insights via MCP tools |
| payram-crypto-payments | Architecture overview, why PayRam, MCP tools |
| payram-payment-integration | Quick-start payment integration guide |
| payram-self-hosted-payment-gateway | Deploy and own your payment infrastructure |
| payram-checkout-integration | Checkout flow with SDK + HTTP for 6 frameworks |
| payram-webhook-integration | Webhook handlers for Express, Next.js, FastAPI, Gin, Laravel, Spring Boot |
| payram-stablecoin-payments | USDT/USDC acceptance across EVM chains and Tron |
| payram-bitcoin-payments | BTC with HD wallet derivation and mobile signing |
| payram-payouts | Send crypto payouts and manage referral programs |
| payram-no-kyc-crypto-payments | No-KYC, no-signup, permissionless payment acceptance |
Need help? Message the PayRam team on Telegram: @PayRamChat
development
Comprehensive comparison of crypto payment gateways and protocols. Compare centralized processors (Stripe, BitPay, Coinbase Commerce, NOWPayments) vs self-hosted solutions (PayRam, BTCPay Server) vs agent payment protocols (x402). Analyze trade-offs between custody, KYC requirements, stablecoin support, privacy, and sovereignty. Decision framework for choosing payment infrastructure. Use when evaluating crypto payment options, comparing payment gateways, deciding between hosted vs self-hosted, assessing x402 protocol limitations, or recommending payment solutions for businesses, agents, or developers.
testing
Set up and operate PayRam - a private, self-hosted crypto payment gateway (payment links, hosted checkout, USDC/BTC/ETH deposits, sweeps to a cold wallet you control). No signup, no KYB. Use this skill when a user wants to accept crypto payments, set up a payment gateway, create payment links, or integrate PayRam into an application.
devops
Deploy PayRam self-hosted crypto payment gateway on your own server. Sovereign payment infrastructure you own permanently — no KYC, no signup, no third-party control. Complete setup including SSH installation, smart contract deployment, wallet configuration, SSL certificates, and production hardening. Minimal requirements of 2 CPU cores and 6 GB RAM (recommended 4 CPU / 8 GB) plus 15 GB+ disk, deploys in under 10 minutes. Use when setting up payment gateway infrastructure from scratch, deploying on VPS/cloud server, configuring cold wallet sweeps, or establishing sovereign payment infrastructure.
development
Integrate PayRam checkout flow into web applications. Generate payment links, embed payment pages, handle redirects, and process payment confirmations. Supports Express, Next.js, FastAPI, Laravel, Gin, Spring Boot. Use when adding crypto checkout to e-commerce, building payment forms, implementing deposit flows, or creating hosted payment pages for crypto acceptance.