Real-time webhook notifications sent when payments are received via custom payment flow. CryptAPI sends two types of webhooks: pending (payment detected in mempool) and confirmed (payment verified by blockchain).
Method: You choose when creating the payment:
GET(default) - Webhook data sent as URL query parametersPOST(setpost=1) - Webhook data sent in request body
Content-Type: You choose when creating the payment:
application/x-www-form-urlencoded(default) - Standard form encodingapplication/json(setjson=1) - JSON format in request body
Custom parameters location
Custom parameters you append to the webhook URL passed in the callback parameter (e.g., order_id, user_id) are delivered as URL query parameters. They are available via the request's query string even when you set post=1 and/or json=1.
Tracking Payments with Custom Parameters
Important: Add Custom Parameters to Your Webhook URL
When creating custom payment flow payments, always add your own query parameters to the webhook URL you pass in the callback parameter to track which order or user the payment belongs to. For example, adding ?order_id=123 or ?user_id=456 will ensure that these parameters are included in the webhook payload.
This is the recommended way to link a CryptAPI payment to a specific order or user in your system.
Webhook Types
Pending Webhook
Sent when a payment is detected in the blockchain mempool but not yet confirmed. Particularly useful for slower blockchains (Bitcoin, Litecoin, Bitcoin Cash) to provide immediate user feedback.
Confirmed Webhook
Sent when a payment receives the specified number of confirmations. The transaction is considered final and funds are forwarded to your address.
Webhook Fields
Common Fields (Both Pending & Confirmed)
These fields are included in both pending and confirmed webhooks:
uuidstring•Required
Unique identifier for each payment transaction. Use this to prevent processing duplicate webhooks.
Important: Always store and check this UUID in your database before processing any payment to avoid duplicates.
address_outstring•Required
Your destination address(es) where CryptAPI forwards the payment.
Format:
- Single address:
1H6ZZpRmMnrw8ytepV3BYwMjYYnEkWDqVP - Multiple addresses:
{1H6ZZpRmMnrw8ytepV3BYwMjYYnEkWDqVP: 0.70, 1PE5U4temq1rFzseHHGE2L8smwHCyRbkx3: 0.30}
coinstring•Required
Cryptocurrency ticker used for the payment.
Format:
- Native coins:
btc,eth,ltc,bch,trx - ERC-20 tokens:
erc20_usdt,erc20_usdc - TRC-20 tokens:
trc20_usdt,trc20_usdc - BEP-20 tokens:
bep20_usdt,bep20_usdc - Polygon tokens:
polygon_usdt,polygon_usdc
pendinginteger•Required
Indicates webhook type:
1= Pending webhook (payment detected, not confirmed)0= Confirmed webhook (payment verified and forwarded)
Confirmed Webhook Only Fields
These additional fields are only included in confirmed webhooks (pending=0):
value_coinnumber•Required
Payment amount sent by your customer before any fees are deducted.
Note: For the amount after fees, use value_forwarded_coin
value_coin_convertstring
JSON-encoded object with FIAT currency conversions of value_coin.
Availability: Only when convert=1 was set during payment creation
Format: {"USD": "3.20", "EUR": "3.05", "GBP": "2.62", "CAD": "4.16", ...}
value_forwarded_coinnumber•Required
Amount forwarded to your address after CryptAPI fees are deducted.
value_forwarded_coin_convertstring
JSON-encoded object with FIAT currency conversions of value_forwarded_coin.
Note: Only present when convert=1 was set during payment creation
Format: {"USD": "3.17", "EUR": "3.01", "GBP": "2.59", "CAD": "4.12", ...}
Security: Verify Webhook Signatures
Security Warning! Always verify incoming webhook signatures. For a complete guide, see our Verify Webhook Signatures Guide.
Reliability and Best Practices
For important information on how CryptAPI handles webhook delivery, retries, and timeouts, along with essential best practices for building a reliable webhook handler, please see our main guide.
➡️ Read the How Webhooks Work Guide
Implementation Examples
// Express.js webhook handler (supports both GET and POST)
app.all('/webhook', express.json(), (req, res) => {
// 1. Verify the webhook signature
if (!verifyWebhookSignature(req)) {
return res.status(401).send('Unauthorized');
}
// 2. Extract data
// Body fields (depending on post=1/json=1)
const source = req.method === 'GET' ? req.query : req.body;
const { uuid, pending, value_coin, coin } = source;
// Custom parameters are ALWAYS sent as query parameters
const { order_id, user_id } = req.query;
// 3. Check for duplicates using UUID
if (isWebhookAlreadyProcessed(uuid)) {
return res.status(200).send('*ok*');
}
// 4. Handle based on webhook type
if (pending === 1) {
// Handle pending payment
notifyUser(user_id, 'Payment detected, awaiting confirmation...');
} else if (pending === 0) {
// Handle confirmed payment
processSuccessfulPayment({
uuid,
value_coin,
coin,
orderId: order_id,
userId: user_id
});
}
// 5. Mark this webhook as processed using the UUID
markWebhookAsProcessed(uuid);
// Always respond to stop retries
res.status(200).send('*ok*');
});